Last updated on October 10, 2022
As Rubyists, we all know that ruby gems can make coding a little bit easier and this one is no different. Active Model Serializer is by far my favorite gem. In this post, I’ll show you why this gem is a must-have to develop better APIs and if you already use it but want to have a better understanding of how it works, this post is also for you.
Active Model Serializer is a Ruby gem that serializes data. A serializer allows you to decide which attributes and relationships will be rendered on your API which makes it possible to customize JSON data.
By default, rails API renders all data and this can be a problem. There is data you probably won’t need to render and another common problem is an increase in data over time, which can lead to slow page loading time. As you know, slowness accumulates over time! Take a look at this JSON response. All relationships and attributes are being rendered.
[
{
"id": 1,
"author_id": 1,
"illustrator_id": 1,
"name": "Dona Flor and Her Two Husbands",
"year": 1966,
"created_at": "2022-08-23T14:14:44.313Z",
"updated_at": "2022-08-23T14:14:44.313Z"
},
{
"id": 2,
"author_id": 1,
"illustrator_id": 1,
"name": "Tent of Miracles",
"year": 1985,
"created_at": "2022-08-23T14:14:44.323Z",
"updated_at": "2022-08-23T14:14:44.323Z"
},
{
"id": 3,
"author_id": 1,
"illustrator_id": 1,
"name": "Gabriela, Clove and Cinnamon",
"year": 1958,
"created_at": "2022-08-23T14:14:44.342Z",
"updated_at": "2022-08-23T14:14:44.342Z"
}
]
ActiveModelSerializers is all about convention over configuration. This is a software design paradigm that makes a developer’s life easier because we don’t have to take too many decisions. As long as you follow certain conventions you don’t need to add additional configuration.
This gem is composed of serializers (responsible for which attributes and relationships are serialized) and adapters (how these attributes and relationships will be rendered). As I said before, the gem customizes which and how attributes and relationships should appear and does this through serializers and adapters, respectively.
Let’s start our rails API application by running
rails new bookstore --api
bundle install
Great! Now we can create our models
rails g model author
rails g model illustrator
rails g model book author:references illustrator:references
Don’t forget to set the routes.rb
Rails.application.routes.draw do
resources :books
resources :authors, only: [:index, :show]
resources :illustrators, only: [:index, :show]
end
And our controllers with their relationships:
rails g controller authors
class AuthorsController < ApplicationController
def index
render json: Author.all
end
def show
render json: Author.find(params[:id])
end
private
def author_params
params.require(:author).permit(:name)
end
end
rails g controller illustrators
class IllustratorsController < ApplicationController
def index
render json: Illustrator.all
end
def show
render json: Illustrator.find(params[:id])
end
private
def illustrator_params
params.require(:illustrator).permit(:name)
end
end
rails g controller books
class BooksController < ApplicationController
def index
render json: Book.all
end
def show
render json: Book.find(params[:id])
end
private
def book_params
params.require(:book).permit(:name, :year, :author_id)
end
end
rails db:create db:migrate
Let’s populate our database in seeds.rb with test data:
jorge_amado = Author.create(name: "Jorge Amado")
floriano_teixeira = Illustrator.create(name: 'Floriano Teixeira')
dona_flor = Book.create({ name: 'Dona Flor and Her Two Husbands', year: 1966, author_id: jorge_amado.id, illustrator_id: floriano_teixeira.id })
tent = Book.create({ name: 'Tent of Miracles', year: 1985, author_id: jorge_amado.id, illustrator_id: floriano_teixeira.id })
gabriela = Book.create({ name: 'Gabriela, Clove and Cinnamon', year: 1958, author_id: jorge_amado.id, illustrator_id: floriano_teixeira.id })
Ok, now with everything set up, let’s see how the data is being rendered for Author, Book, and Illustrator
rails s
[
{
"id": 1,
"author_id": 1,
"illustrator_id": 1,
"name": "Dona Flor and Her Two Husbands",
"year": 1966,
"created_at": "2022-08-23T14:14:44.313Z",
"updated_at": "2022-08-23T14:14:44.313Z"
},
{
"id": 2,
"author_id": 1,
"illustrator_id": 1,
"name": "Tent of Miracles",
"year": 1985,
"created_at": "2022-08-23T14:14:44.323Z",
"updated_at": "2022-08-23T14:14:44.323Z"
},
{
"id": 3,
"author_id": 1,
"illustrator_id": 1,
"name": "Gabriela, Clove and Cinnamon",
"year": 1958,
"created_at": "2022-08-23T14:14:44.342Z",
"updated_at": "2022-08-23T14:14:44.342Z"
}
]
[
{
"id": 1,
"name": "Jorge Amado",
"hometown": "Itabuna - Bahia - Brazil",
"created_at": "2022-08-23T14:14:44.268Z",
"updated_at": "2022-08-23T14:14:44.268Z"
}
]
http://localhost:3000/illustrators
[
{
"id": 1,
"name": "Floriano Teixeira",
"hometown": "Cajapió - Maranhão - Brazil",
"created_at": "2022-08-23T14:14:44.281Z",
"updated_at": "2022-08-23T14:14:44.281Z"
}
]
As you can see this is how the Rails API renders the JSON by default. What if we don’t need “created_at” or “updated_at”? And if we prefer to exhibit our author’s and illustrator’s names rather than author_id and illustrator_id? The Active Model Serializer gem comes to the rescue.
Add this to our Gemfile , then run bundle install
gem 'active_model_serializers', '~> 0.10.13'
bundle install
Head over to Rubygems for more info.
Let’s create a serializer for the Books model.
rails g serializer Book
A new folder will be created in app > serializers > book_serializer.rb
class BookSerializer < ActiveModel::Serializer
attributes :id
end
Go back to our http://localhost:3000/books and let’s check it out. This is a great example of convention over configuration. Once we run rails g serializer Book, it creates the serializer and renders it as it is.
[
{
"id": 1
},
{
"id": 2
},
{
"id": 3
}
]
Now, we can add the attributes we would like to render, in this situation name and year. You can leave it out or place whatever attribute you need.
class BookSerializer < ActiveModel::Serializer
attributes :id, :name, :year
end
And now we have:
[
{
"id": 1,
"name": "Dona Flor and Her Two Husbands",
"year": 1966
},
{
"id": 2,
"name": "Tent of Miracles",
"year": 1985
},
{
"id": 3,
"name": "Gabriela, Clove and Cinnamon",
"year": 1958
}
]
What about rendering each book’s author? In the book serializer, I let the relationship explicit by adding belongs_to: author
class BookSerializer < ActiveModel::Serializer
attributes :id, :name, :year
belongs_to: author
end
Just like magic, now we have all the Author’s attributes
[
{
"id": 1,
"name": "Dona Flor and Her Two Husbands",
"year": 1966,
"author": {
"id": 1,
"name": "Jorge Amado",
"hometown": "Itabuna - Bahia - Brazil",
"created_at": "2022-08-23T14:14:44.268Z",
"updated_at": "2022-08-23T14:14:44.268Z"
}
},
{
"id": 2,
"name": "Tent of Miracles",
"year": 1985,
"author": {
"id": 1,
"name": "Jorge Amado",
"hometown": "Itabuna - Bahia - Brazil",
"created_at": "2022-08-23T14:14:44.268Z",
"updated_at": "2022-08-23T14:14:44.268Z"
}
},
{
"id": 3,
"name": "Gabriela, Clove and Cinnamon",
"year": 1958,
"author": {
"id": 1,
"name": "Jorge Amado",
"hometown": "Itabuna - Bahia - Brazil",
"created_at": "2022-08-23T14:14:44.268Z",
"updated_at": "2022-08-23T14:14:44.268Z"
}
}
]
Let’s continue with our serialization by choosing which Author’s attributes we want to render. We can start by creating a serializer for the Author model.
rails g serializer author
class AuthorSerializer < ActiveModel::Serializer
attributes :id
end
“created_at” and “updated_at” are no longer needed data, so we can leave them and place name and hometown
class AuthorSerializer < ActiveModel::Serializer
attributes :id, :name, :hometown
end
Now we have all the data we need:
[
{
"id": 1,
"name": "Dona Flor and Her Two Husbands",
"year": 1966,
"author": {
"id": 1,
"name": "Jorge Amado",
"hometown": "Itabuna - Bahia - Brazil"
}
},
{
"id": 2,
"name": "Tent of Miracles",
"year": 1985,
"author": {
"id": 1,
"name": "Jorge Amado",
"hometown": "Itabuna - Bahia - Brazil"
}
},
{
"id": 3,
"name": "Gabriela, Clove and Cinnamon",
"year": 1958,
"author": {
"id": 1,
"name": "Jorge Amado",
"hometown": "Itabuna - Bahia - Brazil"
}
}
]
As simple as it is, we just customized our JSON response. Let’s compare with and without using the Active Model Serializer:
With serializers:
[
{
"id": 1,
"name": "Dona Flor and Her Two Husbands",
"year": 1966,
"author": {
"id": 1,
"name": "Jorge Amado",
"hometown": "Itabuna - Bahia - Brazil"
}
},
{
"id": 2,
"name": "Tent of Miracles",
"year": 1985,
"author": {
"id": 1,
"name": "Jorge Amado",
"hometown": "Itabuna - Bahia - Brazil"
}
},
{
"id": 3,
"name": "Gabriela, Clove and Cinnamon",
"year": 1958,
"author": {
"id": 1,
"name": "Jorge Amado",
"hometown": "Itabuna - Bahia - Brazil"
}
}
]
Without serializers:
[
{
"id": 1,
"author_id": 1,
"illustrator_id": 1,
"name": "Dona Flor and Her Two Husbands",
"year": 1966,
"created_at": "2022-08-23T14:14:44.313Z",
"updated_at": "2022-08-23T14:14:44.313Z"
},
{
"id": 2,
"author_id": 1,
"illustrator_id": 1,
"name": "Tent of Miracles",
"year": 1985,
"created_at": "2022-08-23T14:14:44.323Z",
"updated_at": "2022-08-23T14:14:44.323Z"
},
{
"id": 3,
"author_id": 1,
"illustrator_id": 1,
"name": "Gabriela, Clove and Cinnamon",
"year": 1958,
"created_at": "2022-08-23T14:14:44.342Z",
"updated_at": "2022-08-23T14:14:44.342Z"
}
]
Yes, the Active Model Serializer is a very powerful gem allowing you to even serialize methods. On our book’s table, there is a column called ‘year’ which refers to the book’s first year of publication. Let’s supposed I want to know how old is this book. In the book serializer, I created a very simple method called ‘book_age’ to calculate how old the book is and to make it render in our book’s endpoint, I just need to add this method as an attribute. Let’s check it out:
class BookSerializer < ActiveModel::Serializer
attributes :id, :name, :year, :book_age
belongs_to :author
def book_age
Date.today.year - (object.year)
end
end
And here is the result:
[
{
"id": 1,
"name": "Dona Flor and Her Two Husbands",
"year": 1966,
"book_age": 56,
"author": {
"id": 1,
"name": "Jorge Amado",
"hometown": "Itabuna - Bahia - Brazil"
}
},
{
"id": 2,
"name": "Tent of Miracles",
"year": 1985,
"book_age": 37,
"author": {
"id": 1,
"name": "Jorge Amado",
"hometown": "Itabuna - Bahia - Brazil"
}
},
{
"id": 3,
"name": "Gabriela, Clove and Cinnamon",
"year": 1958,
"book_age": 64,
"author": {
"id": 1,
"name": "Jorge Amado",
"hometown": "Itabuna - Bahia - Brazil"
}
}
]
Well, these are all small examples with just a few data points. Now imagine an application with lots of relationships and large models. With the Active Model Serializer gem, you can manage which attributes will be rendered and how they are rendered in a very simple way by using Active model serializers.
Articles, guides and interviews about web development and career progression.
Max 1-2x times per month.