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.
The problem
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.
Solution
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 JsonApiResponders
.
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
respond_with
method.
In 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.
Error Handling
One of the built-in features of JsonApiResponders is the ability to handle rescuing exceptions. At this point, it will rescue ActiveRecord::RecordNotFound
and 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 following way.
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
Compatiblity
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
config/initializers folder.
JsonApiResponders.configure do |config|
config.render_method = :jsonapi
end
After you make this change, you are good to go!
Conclusion
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.