Recently I was looking into building an API for an internal company system. The API only had to communicate with a front-end, plus be able to fire of a couple of integration processes. The thought crossing my mind was, since we’re in full control of the stack, surely there’s a better way to communicate than to encode all our info into REST URL endpoints then decode them at the recipient end.
Now, I’ve built REST endpoints with generic payloads & generic returns before, but that’s only the first step. What I was looking for is basically a JSON RPC-like pattern. Then, BAM, this article comes along. If you haven't read the article, basically its a discussion around using a Redux-inspired JSON RPC pattern, dubbed OAPI, for your API’s (with examples given using Node). Since we’re using a Flux-inspired state management library for our front-end, this made perfect sense to me.
I guess the Redux-inspired OAPI inspired me to create a Flux-inspired API inspired by JSON-RPC. Make sense? Cool, be inspired.
* If you’re a little unsure about Flux and why I’ve used it semi-interchangeably with Redux… well the TL;DR is that Flux is a state management pattern for front-ends (for further explanation, have a look atthe flux docs). Redux is merely one implementation of this pattern.
What the Flux
The general gist of Flux is that its a unidirectional data flow. Now that’s something I’m completely disregarding for our use case. I suppose that makes it very loosely Flux-inspired… but hey what are we as devs without cool sounding patterns?
The basic Flux flow
Anyway, the next couple of ideas from Flux are definitely helpful for our pattern (did I mention I called our implementation Apux? API + aspnet + Flux? See, as I said, we need those cool sounding names). Flux has the idea of an ‘action’ —_which, as you can guess, is exactly as it sounds. Its an action we want to perform, with any data we need to do it. If we want to perform an _action, we dispatch it… via, you guessed it, the dispatcher. Again this is exactly as it sounds, it dispatches actions to the appropriate spot.
A-pux for your Flux
So how do we modify this MVC-based data flow pattern for an API? Well lets see what sense we can make from the diagram below.
Our slightly bastardized Flux flow for an API (Apux)
We receive an action (via a http call), throw this action straight to our dispatcher, so it can dispatch it to… the appropriate handler. This is where we slightly differ from Flux. Instead of a global data store, what we actually want is to do something. Now this could be an update to a data-store (e.g. SQL DB), or it could be a request to send an email. Regardless, we want to handle the action. So, ta-da, we’ve got a handler which handles whatever that action is intended to do.
The cool thing we have is that the handler can return either just the action result (which we would return as the http call response) or it could return another action. This allows us to chain action functionality fairly easily.
Show me the Action
So what does an action actually look like in our aspnet API? Well first up we have the interface:
Pretty simple, no? We have a string to identify the action type and a base payload of JSON data. Why a ‘base payload’ you ask? Well that allows us to always have a base type from which we can serialize/deserialize — abusing the Newtonsoft Library to do our heavy lifting. The actual payload will hang off the instantiated object;
This allows us to have a ‘properly’ typed action but still gives us the flexibility of passing around an essentially dynamic object.
Okay, so we can now form a JSON action that looks like something like this:
Then shoot it off to our API. From there we grab the action from a controller and dispatch it:
The rootdispatcher is a special dispatcher that allows us to split the actions via a namespace. This helps us scale the number of actions within the API, whilst keeping the code manageable. So what does the root dispatcher do? well it dispatches actions to the appropriate dispatcher. Dispatchception!
So following our example action JSON, because the action type is PRODUCT_GET_BY_ID we’d end up passing the action to the productaction dispatcher.
Can you handle that
The product dispatcher then finally hands the action to the appropriate action handler for that action. The actionhandler does some work, calling for example some business logic, or data access, then returns an action result.
An Apux action result looks very similar to an Apux action, with a couple of minor extensions:
The action result always has a JToken Payload and it additionally has an array of any errors that occurred whilst executing the action. There is also a Dispatch flag which tells the root dispatcher to dispatch this result as a new action (this is what allows us to chain the actions together).
Finally, we have a result
Once we have the final action result, we return it as a JSON payload that looks something like this:
Our front-end can then deal with this in the same manner it deals with other actions (or if you set it up correctly, can simply slot this in as a new ‘success’ action.)
Awesome, well then you at least found this mildly interesting.. on a serious note, whilst I really like this pattern, it does have its place. I think its a great replacement for building REST API’s that solely support a single app. As mentioned in the article that inspired this work, I think if you’re exposing a generic API to the world, then REST is still your best bet.
Some other benefits of the Apux implementation:
We can wrap a transaction scope for db calls around an entire set of actions
The frontend can share the string constant definitions of the actions within the API
We’ve stopped boxing/unboxing values into URLs/routes and query params between the backend and frontend
We can create small, modular, atomic actions, then chain them together when needed
API Versioning can be easily done at an action level
Some of the downsides I’ve found:
We’re acting outside the usual aspnet MVC controller pattern, meaning its sometimes difficult to use some of the great baked-in controller features.
Because of the above, things like endpoint/action authorisation & authentication become a manual process
A slight overhead is added to each call with all the action switching logic
If you have any other thoughts on the Apux implementation, let me know!