The application we will be working on was built in the previous article in this series. I would recommend you going through that article first. If you do not want to do that, you can clone that project from Github and then check out the
What is RPC?
This was my question when I was reading the Fusion.js documentation. I had never heard of RPC in the context of web development.
RPC in web development is an alternative to RESTful architecture. Rather than defining your urls around nouns and request methods, the urls are defined as verbs. For example, you might have
/api/books as an endpoint in a RESTful API. That single endpoint is used for both retrieving a list of, and creating, books. On the other hand, an RPC endpoint would look like
RPC in Fusion.js
Thanks to Fusion.js’ plugin architecture with its upstream/downstream nature, RPC in Fusion.js is quite interesting. As you will see, the plugin provides us with a helper function called
withRPCRedux which we can compose into our component, providing us a function which fires off an API call using RPC. This is what happens:
- The “handler” function receives the deserialized request body.
- The handler function can do any processing it needs to, including hitting a database.
- The response will be dispatched as a Redux action. I will go into this in more detail later in this article.
- The proper reducer handles the action, updating the Redux state on the client side.
While this seems like a lot, it is not too bad once you have implemented it a few times. That is what we will be doing in this article.
The first thing we need to do is view the readme file for the plugin by visiting https://github.com/fusionjs/fusion-plugin-rpc-redux-react. This file shows us how to install the plugin as well as how to interact with it.
Installing the plugin
If we compare the
src/main.js file to the one in the “Setup” section of the readme, we find that the following lines need to be added to our file:
In addition to adding this configuration to our app, we need to install the plugins we are importing. The
fusion-plugin-react-reduxplugin also depends on
react-redux, which depends on
redux, so we will install those as well. To do that, run
yarn add fusion-plugin-universal-events fusion-plugin-react-redux fusion-plugin-rpc-redux-react fusion-tokens unfetch react-redux redux.
After installing the node modules, update the
If you still have
yarn dev running, you will notice that you now have an error saying that
redux/index.js are missing. This is a perfect example of how awesome Fusion.js’ Hot Module Reloading (HMR) is!
Creating our first handler
The setup code we added included this:
What this does is register
handlers to the
RPCHandlersToken dependency injection token when it is run on the server side and registers the
unfetch library to the
FetchToken token on the client (browser) side. Any time these tokens are used in dependency injection, the registered values (libraries, functions, objects, etc) will be supplied for use.
As a side note, we are registering a
FetchToken because the
fusion-plugin-rpc-redux-react plugin automagically uses it on the browser side to hit the API endpoints for each RPC handler, while the handler function is called directly on the server during server side rendering.
handlers value comes from importing
rpc/index.js, so that is where we should put our RPC handlers. The file will export an object with key names that will be the names of our handler functions. These names are important, and will be used to reference the handler in other parts of our codebase. The value for each key needs to be an async function which returns an object that will be serialized and sent back to the client. This returned value will be received in our Redux actions as
This is an example of a “no op” handler. It simply receives the request body, destructures
value and sends it on to redux. If no server side processing needs to happen, this is a common way to handle it. As you can see, the handler also receives the context, which gives it access to the to HTTP request and response.
We will leave our handler in this state for now, and come back to it in a little while.
Creating our first reducer
The other thing we registered is
ReducerToken, so we need to take care of that next. Our
redux/index.js file should export a standard Redux reducer. It can be built using
reduceReducers, or just a regular reducer function. There is nothing special about reducers in Fusion.js apps.
There is a special thing that happens as a result of using the RPC plugin though. When we start our RPC call, a Redux action of type
<HANDLER_NAME>_START is fired. Next, our handler function is called. If an error is thrown in our handler function, it is caught and
<HANDLER_NAME>_FAILURE is dispatched. If no error is thrown,
<HANDLER_NAME>_SUCCESS is dispatched. You can see the source code responsible for that here.
To start with, we will write a traditional reducer function. Here are the reducer files:
Since we don’t want to make any changes when the action starts or fails, we simply return the current state. In a production app it would be a good idea to bring errors to the surface by adding them to the state in the failure clause so they can be displayed.
We could also leave the
_FAILURE clauses out since they are identical to the
else clause, but I have included them for comparison against the next reducer we will create.
Creating the Higher Order Component (HOC)
In case you don’t know about them, higher order components are components that return other components. We will be using
withRPCRedux to compose a component which has access to our RPC action.
The change will be made in
Quite a bit has changed here, so let’s go through it one item at a time.
First, we are importing
compose from the
redux library. If you are not familiar with it, I recommend reading the docs about it. Next, we import
react-redux, which is used to inject our Redux state into our component. The last new import is
withRPCRedux, which is used to inject our RPC handler function into our component as a prop.
Input component has now been promoted from a direct export to a variable in order to use it in our HOC. Last, we have composed the
withRPCRedux function calls into a HOC function which accepts our
Connecting the Redux state to current value
The next thing we need to do is update our component to use the new props we have provided to it:
It has been updated to destructure the
increment props. Those props have been used to display the value as well as the in
onClick handler to call the RPC action. You can see that we are passing an object to
increment for serialization as I mentioned we would be doing.
Try it out
You should now be able to refresh the browser (if it didn’t refresh itself, which it may have) and click the
+ button to see it increment the value!
An alternative way to increment
We could have also incremented (or done any other work) on the server side. To see this, let’s update our handler and make our number change by a total of 10 by changing it on the server side in addition to the client side:
Head back to your browser and without even refreshing you will find that your value now increments by 10! This change was just an example and will not be in the rest of the code in this series.
First, we need to add a handler:
Next we will handle the Redux actions. This time, we will use
createRPCReducer so we can compare it against our vanilla reducer.
createRPCReducer is a function which is provided by
fusion-plugin-rpc-redux-react. It needs to be called with an RPC action name, an object containing
failure reducer functions, and an optional initial state.
It has been added to the
value.js file here:
There are a couple of things going on, but we will focus on the
decrement object first. The
decrement reducers are doing exactly the same thing. The use of
createRPCReducer is more concise, but there is nothing wrong with the verbose implementation if you prefer it.
The next thing you should notice is that we have added
reduceReducers. This is a function from the
reduce-reducers library (which you need to
yarn add), that calls each of the function arguments in succession passing the return value of the last function into the next function. This allows us to have sibling reducers which work on the same piece of state.
Having sibling reducers work on the same piece of state was probably the thing I struggled with the most. It is amazing to see such a simple solution!
Connecting the button
Once again, we need to update the
We have added the
decrement prop as well as using used it in the
onClick handler. We also composed it into our component once again using
That’s it for this part of the app. The only thing left to do is make our event log work, which we will tackle in the next article.
Another side note
Anything which is
register()ed in your app can either be the object or function that the dependency token needs, or a plugin that
provides() it. That means our
src/rpc/index.js file could also look like this:
In fact, you can update your file and you will see it still functions exactly the same.
To continue building the application, head to Connecting our event log.
An introduction to Fusion.js
Building a basic app in Fusion.js
Using RPC with Fusion.js and Redux (this article)
Connecting our event log
Comparing RPC reactors to RPC reducers in Fusion.js
Deploying your Fusion.js application to Heroku