11 July 2020
Welcome to the fourth chapter of the API-only rails course. In this part, we will integrate the
activemodel serializers to our project. If you didn’t read the previous chapters of the course, you can check all content from the course page.
In the previous chapter, we have created the first version of the user creation endpoint. If we send a new request to create a user, the API response looks as follows.
By default, ruby on rails adds all columns of the model to the API response when we render a resource with JSON format. But let’s imagine we don’t want to expose
updated_at value from the API endpoint for the created user. The simplest way of doing it without using any library is overriding the
as_json method in the model, which is used by rails internally while serializing ruby objects. Let’s modify our
User model to see it in action.
Inside of the
as_json method, we defined the fields we want to see in the response payload and called super method to get standard behavior from the
ActiveRecord::Base with specified fields. (We also merged with options parameter to keep other options like status code.) If we send another request to create a new user now:
We will get an API response without an
But as you can imagine, we would like to manipulate API payloads that will have much more complexity than the previous example. We will use nested associations with different data structures, and it will become hard to maintain with overriding the
To solve the problem we mentioned in a structured and straightforward way, we can use other libraries to manipulate the JSON structure of the API responses. We have different options like fast_jsonapi and jbuilder for serializing ruby objects. However, I would like to use active_model_serializers gem because it’s mature enough to have all functionality needed for serializers. But instead of using the latest version of the activemodel serializer
(0.10.10), I will use version
0.8.4 because of performance reasons.
active_model_serializers to the
Gemfile and run
bundle install command.
Then let’s create
app/serializers/user_serializer.rb file and add
updated_at fields to the serializer.
So if we send this request with
curl, we will create a new user and see the user’s payload as we specified in the user serializer. (Don’t forget to remove the
as_json method we defined at the beginning of the chapter for demonstration purposes.)
With the new serializer, the API response should be as following.
But how activemodel serializer knows which serializer class to use? It basically looks for the resource class name and searches for the serializer with the same name. For our use case, it looks for
UserSerializer class. We also have an option to pass serializer name explicitly like
render json: @user, serializer: UserSerializer in the controllers.
As you can see, activemodel serializers automatically adds the serializer name as a root key. Still, keep in mind that when we introduce the
renderer module in the next chapters, we will refactor the root key part, and our API responses will have a general abstraction with two root fields as
meta as follows.
We completed the activemodel serializer integration, and it’s working as expected. But I realized that I would like to use
updated_at fields for all of my serializers. To do that, I can create a
BaseSerializer (similar to
BaseController for API controllers) and extend other serializers from the base serializer.
So we can refactor
UserSerializer with removing
updated_at fields and extending it from
We successfully completed the integration of base serializer and used the user serializer inherited from base serializer.
We can complete our serializer implementation with writing tests for both base and user serializers. Let’s start with building
spec/factories/users.rb file since we will use the user factory to test serializers. The user model has presence validation for both
password fields so that we can add those fields to the factory file.
We filled the password with the static value, but we used sequence for the email because we want to differentiate email addresses since they have to be unique, and we might create more than one user in the same spec. You can have more information about sequences from FactoryBot’s github page.
Now we can start writing the base serializer test.
We have created a resource (we used
User because we only have the user model at the moment) and checked whether its
updated_at values matched with the serialized resource.
We can also use the same approach while testing user serializer.
We also need to refactor creating a new user test case inside of
spec/requests/api/v1/users_spec.rb file since active model serializers added the
user as a root key.
And now we can run
bundle exec rspec command to see all tests are green.
Lastly, I want to mention that we can use
build methods directly instead of using them as
FactoryBot.build(...). To do that, we need to modify
spec/rails_helper.rb file as following.
And now, let’s remove the
Now, we will run the
rspec command again to make sure everything works as they were before.
In this chapter, we integrate the activemodel serializer into the project. We also created the base and user serializers with corresponding spec files. You can find the source code of bookmarker application on github. In the next chapter, we will introduce the
renderer module for API controllers. You can also find the previous chapter here.