These days, people are crazy about single page applications. And for good reason ! But building this kind of application requires a backend that can communicate through JSON. Let’s see how we can do that with Rails.
We are going to build a simple API that will return artists, their albums and their songs. Now, the question is how should we generate our models’ JSON.
We could use Rails’ method ‘to_json’ to simply produce a JSON of our models’ attributes. But that means no control over what we are sending. We could also add a ‘as_json’ method to our models, but this means that our models have to handle their presentation layer. What ? Who said Separation of concerns ? If we want to code clean, the presentation layer should be handle by some kind of ‘presenter’ class, not by the model directly. Here comes Active Model Serializers !
Active Model Serializers lets us define ‘serializers’ classes that can handle the JSON generation for your models. Let’s see how !
Source Code
Setup the application
Since I am building an API here, I will use the rails-api gem.
rails-api new serializers_tuto
If you don’t have rails-api, you can install it with gem install rails-api. You can also use a standard Rails app if you prefer.
Create the models
Let’s get started. We are going to create our models :
rails g model Artist name:string label:string
rails g model Album name:string release_date:date artist_id:integer
rails g model Song name:string release_date:date lyrics:text album_id:integer
Migrate all that :
rake db:migrate
We have to add relations between our models :
# app/models/artist.rb
class Artist < ActiveRecord::Base
has_many :albums
end
# app/models/album.rb
class Album < ActiveRecord::Base
belongs_to :artist
has_many :songs
end
# app/models/song.rb
class Song < ActiveRecord::Base
belongs_to :album
end
Active Model Serializers
It’s time to add Active Model Serializers to create our model serializers !
# Gemfile.rb
# ...
gem 'active_model_serializers'
# ...
‘bundle install’ and restart your application.
We’re going to create our serializers manually. Create the folder ‘app/serializers’ and a first file named ‘artist_serializer.rb’.
# app/serializers/artist_serializer.rb
class ArtistSerializer < ActiveModel::Serializer
# Attributes we want to see in our JSON
attributes :id, :name, :label
end
The Artist Controller
Let’s add a controller for the artists :
# app/controllers/artists_controller.rb
class ArtistsController < ApplicationController
def index
render json: Artist.all
end
end
Update the routes :
Rails.application.routes.draw do
resources :artists
root to: 'artists#index'
end
Some data
And create a few records (you can paste the following in db/seeds.rb and run ‘rake db:seed’) :
muse = Artist.create( name: 'Muse', label: 'Warner Bros.')
black = muse.albums.create( name: 'Black Holes and Revelations', release_date: '03/07/2006' )
resistance = muse.albums.create( name: 'The Resistance', release_date: '11/09/2009' )
["Take a Bow", "Starlight", "Supermassive Black Hole", "Map of the Problematique", "Soldier's Poem", "Invincible", "Assassin", "Exo-Politics", "City of Delusion", "Hoodoo", "Knights of Cydonia"].each do |song|
resistance.songs.create( name: song, release_date: resistance.release_date, lyrics: '...' )
end
red = Artist.create( name: 'Red Hot Chili Peppers', label: 'EMI')
californication = red.albums.create( name: 'Californication', release_date: '08/06/1999' )
["Around the World" , "Parallel Universe", "Scar Tissue", "Otherside", "Get on Top" , "Californication", "Easily" , "Porcelain", "Emit Remmus", "I Like Dirt", "This Velvet Glove", "Savior" , "Purple Stain" , "Right on Time", "Road Trippin"].each do |song|
californication.songs.create( name: song, release_date: californication.release_date, lyrics: '...' )
end
Head to ‘localhost:3000’ (or whatever your local url is) and you should see :
Yes, it’s that simple !
Embedded Records
Let’s integrate the albums and songs.
# app/serializers/artist_serializer.rb
class ArtistSerializer < ActiveModel::Serializer
# Attributes we want to see in our JSON
attributes :id, :name, :label
has_many :albums
end
# app/serializers/album_serializer.rb
class AlbumSerializer < ActiveModel::Serializer
# Attributes we want to see in our JSON
attributes :id, :name, :release_date
has_many :songs
end
# app/serializers/song_serializer.rb
class SongSerializer < ActiveModel::Serializer
# Attributes we want to see in our JSON
attributes :id, :name, :release_date, :lyrics
end
Refresh the homepage and now you should see all the albums/songs ! Great !
Moar Logic
You can also add some presentation logic to your serializers. For example, if you want to add one field called ‘name_with_artist’ to albums, you can do :
# app/serializers/album_serializer.rb
class AlbumSerializer < ActiveModel::Serializer
# Attributes we want to see in our JSON
attributes :id, :name, :release_date, :name_with_artist
has_many :songs
def name_with_artist
"#{object.name} from #{object.artist.name}"
end
end
Result :
The End
Active Model Serializers allows you to separate your presentation logic to simple ruby objects. It then detect if the model has a serializer and generate a json with it. If the model does not have any serializer, the whole record will be serialized through Rails.