Customizing JSON In Your API
This lesson plan was last verified to have worked with Ruby v2.4.1 and Rails v5.2.0
- Generate and customize Rails Serializers
- Discuss other serialization options, like Jbuilder
- Understand what constitutes presentation logic in the context of serving a JSON API and why formatting in the model is not the right place
On your own, research serializers. In your notebook, write down the answers to these questions:
- What do serializers allow us to do?
- What resources were you able to find? Which seem most promising?
- What search terms did you use that gave the best results?
Serializers allow us to break from the concept of views fully with our API, and instead, mold that data in an object-oriented fashion. We don’t have views to do our dirty work for us anymore, so we rely on serializers in order to present to whomever is consuming our API what we want them to see.
When we call
render json:, Rails makes a call to
as_json under the hood unless we have a serializer set up. Eventually,
to_json and our response is generated.
With how we’ve used
render json: up til now, all data related with the resources in our database is sent back to the client as-is.
Let’s imagine that you don’t just want the raw guts of your model converted to JSON and sent out to the user – maybe you want to customize what you send back.
Adding to Our Existing Project
We’re going to start where we left off in the internal API testing lesson. Feel free to use the repository that you created yesterday. Otherwise, you can clone the repo below as a starting place.
git clone https://github.com/turingschool-examples/building_internal_apis.git bundle git checkout building_api_complete
We want to work with objects that have related models, so let’s add an
rails g model order order_number rails g model order_item order:references item:references item_price:integer quantity:integer bundle exec rake db:migrate
Add relationships to your models:
# in item.rb has_many :order_items has_many :orders, through: :order_items # in order.rb has_many :order_items has_many :items, through: :order_items
And whip together a quick seed file:
10.times do Item.create!( name: Faker::Commerce.product_name, description: Faker::ChuckNorris.fact, ) end 10.times do Order.create!(order_number: rand(100000..999999)) end 100.times do OrderItem.create!( item_id: rand(1..10), order_id: rand(1..10), item_price: rand(100..10000), quantity: rand(1..10) ) end
bundle exec rake db:seed
Create your controller:
rails g controller api/v1/orders index show
- Note that the generator throws in some routes at the top. This is not great.
showmethods to render appropriate json.
Use Postman or your browser to view the current responses that your API is providing to the routes listed below:
So we have our responses from our server, but it isn’t JSON API 1.0 And it has this created at and updated at stuff which we don’t want. So what do we do? We need to use a serializer.
Using FastJSONAPI to modify
Add this line to your Gemfile.
We can now use the built in generator in order to make ourselves a serialized.
rails g serializer Order id order_number
This will add the appropriate attributes from the Order model. And give us only the id and order number.
Let’s check out what is in the Serializer.
class OrderSerializer include FastJsonapi::ObjectSerializer attributes :id, :order_number end
So now we have this serializer, and we need to modify our controller.
class Api::V1::OrdersController < ApplicationController def index render json: OrderSerializer.new(Order.all) end def show render json: OrderSerializer.new(Order.find(params[:id])) end end
So what we are doing is instead of rendering the ActiveRecord stuff in json, we are sending it to the serializer, where the stuff gets serialized, and then that gets rendered as json.
But what if we wanted to show some awesome relationship action?
class OrderSerializer include FastJsonapi::ObjectSerializer attributes :id, :order_number has_many :items end
Add that to your serializer and refresh.
What if we wanted a custom attribute? We can do so using this format.
Let’s say we wanted an attribute with the number of items
class OrderSerializer include FastJsonapi::ObjectSerializer has_many :items attributes :id, :order_number attribute :num_items do |object| object.items.count end end
This syntax is a bit different from what we are used to. We use
attribute singular, and then as a symbol we pick the name of what we want our attribute to be. We use a do end block similar to an enumerable with a block parameter. Now the block parameter,
object is a lot like self. We get to use it for each single thing of a collection we pass to the serializer. We are essentially saying for each thing you serialize, grab the items and count them too. In this manner we can add a custom generated value for each item.
We can also have a custom static attribute like so:
class OrderSerializer include FastJsonapi::ObjectSerializer has_many :items attributes :id, :order_number attribute :num_items do |object| object.items.count end attribute :greeting do "HELLO FRIENDS" end end
Do what we did to
Order, but on
- Some existing fields
- Some custom fields
- A relationship