Webmachine - REST done right19 Nov 2014
Webmachine describes resources on top of HTTP to provide RESTful APIs. This is pretty much what every modern web framework states, right?
After writing some good amount of controllers on MVC frameworks you realise that it's complicated to keep a clean controller without using callbacks/middlewares to handle things like:
- Conditional redirection;
- Content representation.
On Rails code, for example, It's common to find before_filters (before_action on Rails 4) describing behaviour that should happen before handling the resource itself. Here's an example of discourse's ApplicationController:
These callbacks will run doing assorted things on pretty much every controller on the app. Just by looking on the name of the callbacks you can notice some redirection, authorisation, authentication and content representation logic. One important thing is that the order here matters and you will probably hit lots of issues if you change the order of the before_filters. There's also the problem that unit tests are complex as you need to test each callback indirectly.
Could it be less complicated? Of course!
Webmachine describes the request handling as a state machine where HTTP status codes are end states. It implements the HTTP protocol as a collection of functions that will describe the flow into this state machine. Below you can see the initial state and some transitions:
Here's an example from
This simple resource you will get automatically the following status:
- 405 Method Not Allowed if it's not a GET;
- 406 Not Acceptable if the request does not specify an
Acceptheader that tolerates
- 200 OK if everything is fine with and the correct content-type header will be provided.
Now let's handle when the order does not exist adding the
resource_exists? method to the resource:
Now 404 Not Found will be given if
resource_exists? returns falsy value.
Let's now add authorisation and authentication:
And again easily we will get:
- 401 Unauthorized if
is_authorized?returns falsy value;
- 403 Forbidden if
forbidden?returns falsy value
This is, in my opinion, a scalable way of describing your resources as the code will be as complex as the resources are. The framework already takes care of the nuances of the HTTP protocol keeping the code clean and extremely simple to unit test.