Serializing and JSON API resources in Rails
Serializing, resources in rails
The goal of this article is to understand how to use active_model_serializers
and JSONAPI::Resources
and the reason why we should use them.
Create an api only rails app
What I want to achieve is to use rails as backend talks to frontend via api, that means the rails app should be api only, and since rails already have render :json
out of the box, let’s get the first version of the app working: (I use postgres you don’t have to keep the postgres related part)
You will find the app code generate with this setup is lighter, some directories you see won’t be there anymore, such as app/assets/
, lib/assets/
, app/views/layouts/application.hmtl.erb
and some gems related to the view part are not installed, such as sass-rails
, coffee-rails
, the app/controller/application_controller.rb
looks a little different too, since it is now inheriting from ActionController::API
instead of ActionController::Base
,also the line protect_from_forgery with: :exception
is gone too. (see a more comprehensive list here)
Since the backend and frontend are separate, we will need to enable CORS:
In the Gemfile, uncomment gem 'rack-cors'
, create a new file config/initializers/cors.rb
, it should like this:
Now let’s create some models as a simple example:
update the model files
generate the controller without view assets (note the plural)
Then modify the controller, for this simple example we will just use GET request, update the router:
Let’s create some dummy data in rails console after rake db:migrate:
Now run the rails server and make request to /users
or /pets
api, you should see all attributes are included in the response, including create_at
and update_at
, But we want more control over the response, for instance, we don’t need to show those timestamp to front end.
active model serializers
To get better control of the response, we will install gem 'active_model_serializers'
then use the generator: rails g serializer pet
this will generate a new directory app/serializer
But usually we only need the id of the user in the pet response, we can enhance it with custom functions like this:
There are more ways to add control, such as to make an attribute conditional:
You can also use a different adapters such as JSON api.
JSONAPI::Resources
You might be thinking active model serializer seems to be good enough, but if you consider the JSON API spec, active model serializer(AMS) might not be enough, according to the creator of the JSONAPI::Resources, even though AMS has support for JSON API spec with the adapter, its focus is serializers not resources.
The primary reason we developed JR is that AMS is focused on serializers and not resources. While serializers are just concerned with the representation of a model, resources can also act as a proxy to a backing model. In this way, JR can assist with fetching and modifying resources, and can therefore handle all aspects of JSON API.
let’s see how it works after adding gem 'jsonapi-resources'
and bundle
:
first include the module in controller, it could be in the ApplicationController
or under namespace such as:
some config update in config/environments/development.rb
Create models and relationships similar to the pet and user example.
Create controllers:
Now create app/resources
directory, make resource file for each model in a standard way such as phone_number_resource.rb
, user_resource.rb
The add this in routes.rb:
Now you can create new contact or phone numbers with POST request, then make GET request you should see something like this:
And because we have filter in phone number resources, an API request like this [localhost:7070/phone-numbers?filter[contact]=1](http://localhost:7070/phone-numbers?filter[contact]=1)
would return phone numbers belongs to contact id=1.
You can also get context that is available in controller, for instance:
And you can specify get the underlying model with @model
. You can also specify which attribute is fetchable or updatable, set up filter, pagination, and custom links.