A couple of months ago, we started a new project and decided to use Ember on the front and Rails as an API backend.
We made a decision to make an API according to the JSON API specification since Ember has great support for it, so it was a no-brainer to use the Active Model Serializer gem which also has great support for the JSON API specification.
Early in development, we found ourselves repeating similar code in multiple places in our controllers.
def create user = User.new(user_params) if user.save render json: user, serializer: UserSerializer, status: 201 else render error: user.errors, serializer: ErrorSerializer, status: 422 end end
Not just the create method, but generally, our controller started to have a lot of boilerplate code. Another problem was handling status codes. JSON API standard has strict policies on status codes for each response type, i.e create(201), update(200), error(422) etc.
Handling status codes manually and having a lot of boilerplate code makes the entire process error-prone, and naturally, we wanted to avoid that.
Two gems could potentially make our lives easier.
The first one was RocketPants which we used in our previous projects and we liked it. However, RocketPants isn't compatible with the new version of ActiveModelSerializers and the last commit on RocketPants was two years ago so the project looked abandoned.
The second gem that could potentially handle our problems was Grape. It's great but it would be a full replacement for Rails, and we didn't want to make a tradeoff just like that.
Ultimately, we decided to make our custom solution and we named it
We always liked the syntax of the very popular responders gem which was part of Rails until v4.2. and
since we wanted to start as simple as possible we created our own custom
At the beginning, the method only handled status codes based on the action name. In our controllers, we only use the default CRUD action names such as index, show, create, update and destroy, so we could easily determine which status code to return.
Any other action would lead to the creation of a dedicated controller (which itself only has default CRUD actions). For quite some time we have been managing our controllers like this and we were happy when we found out that DHH does it the same way.
We moved our logic of determining whether the object is valid and which HTTP
status code to return into our
respond_with method, so our controller actions
started to be easy one-liners:
def create respond_with User.create(user_params), serializer: UserSerializer end
Now our controllers started to look much cleaner and more readable and we wanted to bring it a step further - which brings us to error handling.
One of the built-in features of JsonApiResponders is the ability to handle
rescuing exceptions. At this point, it will rescue
ActionController::ParameterMissing errors which we believe are the most occurring ones.
def show respond_with User.find(params[:id]), serializer: UserSerializer end
If the given user id does not exist, JsonApiResponders will catch the error and return a JSON API compliant response with the appropriate error message.
ActionController::ParameterMissing will also be handled automatically in the
def create respond_with User.new(user_params), serializer: UserSerializer end
If there is no
user param sent in the request, ActionController will raise an
error here, which JsonApiResponders will handle automatically.
JsonApiResponders also supports responding with custom errors. You can use the
respond_with_error method to handle any kind of errors. This method will
render an error message as described in the JSON API specification. Below you
can see an example of how it can be used with the Pundit gem:
rescue_from Pundit::NotAuthorizedError do respond_with_error(403, 'Not Authorized') end
JsonApiResponders is compatible with both ActiveModelSerializers and
JsonapiRails gems. If you are using ActiveModelSerializers you don't need to
configure anything, it will work out of the box. However, if you are using
JsonapiRails gem you need to modify
render_method option in JsonApiResponders
configuration. The configuration will most likely be located in
JsonApiResponders.configure do |config| config.render_method = :jsonapi end
After you make this change, you are good to go!
We've been using our JsonApiResponders on multiple projects and are really satisfied with the results. We don't have to handle and think about response codes or whether we’re compliant with the JSON API standard. Everything is handled automatically! We don't have if clauses on create and update methods to check for errors, and there is also simple error handling for record not found and missing parameters.
All of this makes our controllers much cleaner and readable. In most cases, the response will be a one-liner. All in the comfort of Rails Controllers.