Serializer
On the previous page, we demonstrated how to design an API similar to PSGC (Philippine Standard Geographic Code). This API will now be used by our serializer.
In this topic, we will choose only the data we want to see/view of the user, with a single request.
Table of contents
- Why we use Serializer?
- How to Install Serializer?
- Region Serializer
- Province Serializer
- City Serializer
- Barangay Serializer
Why we use Serializer?
Serializer essentially provides an easy way to customize how the JSON is rendered by our controllers
.
Advantages
The Serializer allows us to:
- refactor code to avoid repetition
- render numerous data models from a single controller
- specify which attributes to render
How to Install Serializer?
Reminder:
Make sure that your containers are up and running.
In your gemfile, add gem active_model_serializers
.
gem 'active_model_serializers'
Then run bundle install.
root@0122:/usr/src/app# bundle install
Fetching gem metadata from https://rubygems.org/..........
Resolving dependencies...
...
Installing active_model_serializer 0.10.2
Bundle complete! 10 Gemfile dependencies, 80 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
Region Serializer
Generate region serializer with rails g serializer region
.
root@0122:/usr/src/app# rails g serializer region
create app/serializers/region_serializer.rb
This will create a file named region_serializer.rb
under the app/serializers
directory. Move to the file and introduce the attributes you want to be include in serialization. Suppose we just want to add name
and id
attributes of project while serializing.
Now we create a serializer called RegionSerializer
.
# app/serializer/region_serializer.rb
class RegionSerializer < ActiveModel::Serializer
attributes :id, :name
end
The
attributes
is used to choose a specific column in the table that you want to render incontroller
and the other column is hidden.
In the app/controllers/api/v1
, after we collect the attributes in RegionSerializer
, add the RegionSerializer
to our regions_controller.rb
like this:
# app/controllers/api/v1/regions_controller.rb
class Api::V1::RegionsController < ApplicationController
def index
regions = Address::Region.all
- render json : regions
+ render json : regions, each_serializer: RegionSerializer
end
def show
region = Address::Region.find(params[:id])
- render json : region
+ render json : region, serializer: RegionSerializer
end
end
The different of
each_serializer
andserializer
Theeach_serializer
when you have a collection of records, such asregions
,provinces
,cities
,barangays
you have to iterate over each resource to get a serialized collection of records and theserializer
when you have a single record such asregion
,province
,city
,barangay
no iteration is required, and in return you get a single serialized resource.
This is the differences of with and without serializer
Without Serializer
http://localhost:3000/api/v1/regions
[
{
"id": 1,
"code": "010000000",
"name": "Region I",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
{
"id": 2,
"code": "020000000",
"name": "Region II",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
# ...
]
http://localhost:3000/api/v1/regions/1
{
"id": 1,
"code": "010000000",
"name": "Region I",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
}
With Serializer
http://localhost:3000/api/v1/regions
[
{
"id": 1,
"name": "Region I"
},
{
"id": 2,
"name": "Region II"
},
# ...
]
http://localhost:3000/api/v1/regions/1
{
"id": 1,
"name": "Region I"
}
Now, the result of with
serializer
it see more clearly and easily understood the output of JSON format, than withoutserializer
. The other column is now hidden.
Province Serializer
Generate province serializer with rails g serializer province
.
root@0122:/usr/src/app# rails g serializer province
create app/serializers/province_serializer.rb
This will create a file named province_serializer.rb
under the app/serializers
directory. Move to the file and introduce the attributes you want to be include in serialization. Suppose we just want to add name
, region
and id
attributes of project while serializing.
Now we create a serializer called ProvinceSerializer
.
# app/serializer/province_serializer.rb
class ProvinceSerializer < ActiveModel::Serializer
attributes :id, :region, :name
def region
object.region.name
end
end
You can also add
custom attributes
to change the return value of the attributes depending on what value you need to show.
In the app/controllers/api/v1
, after we collect the attributes in ProvinceSerializer
, add the ProvinceSerializer
to our provinces_controller.rb
like this:
# app/controllers/api/v1/provinces_controller.rb
class Api::V1::ProvincesController < ApplicationController
def index
region = Address::Region.find_by_id(params[:region_id])
provinces = if region
region.provinces
else
Address::Province.all
end
- render json: provinces
+ render json: provinces, each_serializer: ProvinceSerializer
end
def show
province = Address::Province.find_by_id(params[:id])
- render json: province
+ render json: province, serializer: ProvinceSerializer
end
end
This is the differences of with and without serializer
Without Serializer
http://localhost:3000/api/v1/regions/1/provinces
[
{
"id": 1,
"region_id": 1,
"code": "012800000",
"name": "Ilocos Norte",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
{
"id": 2,
"region_id": 1,
"code": "012900000",
"name": "Ilocos Sur",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
#...
]
http://localhost:3000/api/v1/provinces
[
{
"id": 1,
"region_id": 1,
"code": "012800000",
"name": "Ilocos Norte",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
{
"id": 2,
"region_id": 1,
"code": "012900000",
"name": "Ilocos Sur",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
#...
]
http://localhost:3000/api/v1/provinces/1
{
"id": 1,
"region_id": 1,
"code": "012800000",
"name": "Ilocos Norte",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
}
With Serializer
http://localhost:3000/api/v1/regions/1/provinces
[
{
"id": 1,
"region": "Region I",
"name": "Ilocos Norte"
},
{
"id": 2,
"region": "Region I",
"name": "Ilocos Sur"
},
#...
]
http://localhost:3000/api/v1/provinces
[
{
"id": 1,
"region": "Region I",
"name": "Ilocos Norte"
},
{
"id": 2,
"region": "Region I",
"name": "Ilocos Sur"
},
#...
]
http://localhost:3000/api/v1/provinces/1
{
"id": 1,
"region": "Region I",
"name": "Ilocos Norte"
}
City Serializer
Generate region serializer with rails g serializer city
.
root@0122:/usr/src/app# rails g serializer city
create app/serializers/city_serializer.rb
This will create a file named city_serializer.rb
under the app/serializers
directory. Move to the file and introduce the attributes you want to be include in serialization. Suppose we just want to add name
, province
and id
attributes of project while serializing.
Now we create a serializer called CitySerializer
.
# app/serializer/city_serializer.rb
class CitySerializer < ActiveModel::Serializer
attributes :id, :province, :name
def province
object.province.name
end
end
In the app/controllers/api/v1
, after we collect the attributes in CitySerializer
, add the CitySerializer
to our cities_controller.rb
like this:
# app/controllers/api/v1/cities_controller.rb
class Api::V1::CitiesController < ApplicationController
def index
province = Address::Province.find_by_id(params[:province_id])
cities = if province
province.cities
else
Address::City.all
end
- render json: cities
+ render json: cities, each_serializer: CitySerializer
end
def show
city = Address::City.find(params[:id])
- render json: city
+ render json: city, serializer: CitySerializer
end
end
This is the differences of with and without serializer
Without Serializer
http://localhost:3000/api/v1/provinces/10/cities
[
{
"id": 219,
"province_id": 10,
"code": "030801000",
"name": "Abucay",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
{
"id": 220,
"province_id": 10,
"code": "030802000",
"name": "Bagac",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
# ...
]
http://localhost:3000/api/v1/cities/
[
{
"id": 1,
"province_id": 1,
"code": "012801000",
"name": "Adams",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
{
"id": 2,
"province_id": 1,
"code": "012802000",
"name": "Bacarra",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
# ...
]
http://localhost:3000/api/v1/cities/100
{
"id": 100,
"province_id": 4,
"code": "015523000",
"name": "Mabini",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
}
With Serializer
http://localhost:3000/api/v1/provinces/10/cities
[
{
"id": 219,
"province": "Bataan",
"name": "Abucay"
},
{
"id": 220,
"province": "Bataan",
"name": "Bagac"
},
# ...
]
http://localhost:3000/api/v1/cities/
[
{
"id": 1,
"province": "Ilocos Norte",
"name": "Adams"
},
{
"id": 2,
"province": "Ilocos Norte",
"name": "Bacarra"
},
# ...
]
http://localhost:3000/api/v1/cities/100
{
"id": 100,
"province": "Pangasinan",
"name": "Mabini",
}
Barangay Serializer
Generate barangay serializer with rails g serializer barangay
.
root@0122:/usr/src/app# rails g serializer barangay
create app/serializers/barangay_serializer.rb
This will create a file named barangay_serializer.rb
under the app/serializers
directory. Move to the file and introduce the attributes you want to be include in serialization. Suppose we just want to add name
, city
and id
attributes of project while serializing.
Now we create a serializer called BarangaySerializer
.
# app/serializer/barangay_serializer.rb
class BarangaySerializer < ActiveModel::Serializer
attributes :id, :city, :name
def city
object.city.name
end
end
In the app/controllers/api/v1
, after we collect the attributes in BarangaySerializer
, add the BarangaySerializer
to our barangays_controller.rb
like this:
# app/controllers/api/v1/barangays_controller.rb
class Api::V1::BarangaysController < ApplicationController
def index
city = Address::City.find_by_id(params[:city_id])
barangays = if city
city.barangays
else
Address::Barangay.all
end
- render json: barangays
+ render json: barangays, each_serializer: BarangaySerializer
end
def show
barangay = Address::Barangay.find(params[:id])
- render json: barangay
+ render json: barangay, serializer: BarangaySerializer
end
end
This is the differences of with and without serializer
Without Serializer
http://localhost:3000/api/v1/cities/100/barangays
[
{
"id": 2444,
"city_id": 100,
"code": "015523002",
"name": "Bacnit",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
{
"id": 2445,
"city_id": 100,
"code": "015523003",
"name": "Barlo",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
# ...
]
http://localhost:3000/api/v1/barangays
[
{
"id": 1,
"city_id": 1,
"code": "012801001",
"name": "Adams (Pob.)",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
{
"id": 2,
"city_id": 2,
"code": "012802001",
"name": "Bani",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
},
]
http://localhost:3000/api/v1/barangays/86
{
"id": 86,
"city_id": 4,
"code": "012804011",
"name": "Payac",
"created_at": "XXXX-XX-XXTXX:XX:XX.XXXZ",
"updated_at": "XXXX-XX-XXTXX:XX:XX.XXXZ"
}
With Serializer
http://localhost:3000/api/v1/cities/100/barangays
[
{
"id": 2444,
"city": "Mabini",
"name": "Bacnit"
},
{
"id": 2445,
"city": "Mabini",
"name": "Barlo"
# ...
]
http://localhost:3000/api/v1/barangays
[
{
"id": 1,
"city": "Adams",
"name": "Adams (Pob.)"
},
{
"id": 2,
"city": "Bacarra",
"name": "Bani"
},
# ...
]
http://localhost:3000/api/v1/barangays/86
{
"id": 86,
"city": "Bangui",
"name": "Payac"
}
Now we successfully integrate serializer into our application. You can check Serializer for more details.