Accessing relationship data with JSON: API serializer

Sascha Kala
4 min readJan 18, 2021

I’m setting up a Rails API and I wanted to implement a serializer gem.

JSON:API

JSON:API is the currently maintained version of Fast JSON API, a gem created by the lovely folks over at Netflix OSS. Fast JSON API was last updated 3 years ago, however, and JSON:API is the forked and currently maintained edition.

After doing some reading, I decided to use JSON:API over Active Model Serializer because serialization time is 25 times faster. (My app is currently small but I like to plan big.)

Displaying relationship attributes

There are two ways to display attributes for any has_many or belongs_to relationships using JSON:API serializer.

Deeply nested

The simplest way to display attributes of related models is by adding the model name(s) to the list of attributes.

For example, I have a Source model that has_many creators and tags. In order to display the data for those linked creators and tags I can do this in my serializer by adding :creators and :tags to the list of attributes:

class SourceSerializer
include JSONAPI::Serializer
attributes :source_type, :creators, :tags
end

My SourcesController looked like this:

class Api::V1::SourcesController < ApplicationControllerdef index
sources = Source.all
render json: SourceSerializer.new(sources)
end
end

And subsequent rendered data looked like this:

As you can see, the attributes for creators and tags are rendered within the attributes for sources. Additionally, when rendered in this way, the serializer displays every attribute for the related model — including attributes you might not use or need.

Includes and relationship type

The other way to display attributes of related models is by using the relationship that links them. This also requires tweaking the controller and generating serializers for the other models, and will change the layout of how your data is rendered.

Using this method the same SourceSerializer looks like this:

class SourceSerializer
include JSONAPI::Serializer
attributes :source_type
has_many :creators
has_many :tags

end

The SourcesController then needs to be updated to include: the has_many model(s). (Without the addition of include your serializer will only display the data in Figure 1, below.)

The controller looks like this:

class Api::V1::SourcesController < ApplicationController def index
sources = Source.all
options = {
include: [:creators, :tags]
}

render json: SourceSerializer.new(sources, options)
end
end

In order for this to work without throwing an error, you’ll also need to make sure that you’ve created serializers for both the Creator and Tag models, and listed any attributes that you want displayed for either.

My CreatorSerializer:

class CreatorSerializer
include JSONAPI::Serializer
attributes :first_name, :last_name
end

The data rendered using this method looks like this:

Figure 1

You’ll notice that the only nested data displayed inside of Source pertaining to the Creator and Tag models are their id and type. (As mentioned above, if you fail to update your controller with include: [], this is all the data you’ll see for your related model(s), there will be no include object below it.)

Any other attributes are displayed in a separate included object, below the data object. It looks like this:

Including attributes in this way allows for you to have control over which attributes of the other models are displayed. By changing my CreatorSerializer to the following:

class CreatorSerializer
include JSONAPI::Serializer
attributes :first_name, :last_name, :title, :url
end

My serializer will render the additional attributes within the included object on the page, as so:

Which to use?

After doing some more reading, I’ve come to the conclusion that it’s largely personal preference.

Using included is meant to cut down on rendering duplicate data, per this stack overflow thread. This makes sense given that it’s necessary to manually include which attributes of the included model are being rendered. So potentially this is better for very large applications.

I don’t like the layout of how included displays attribute data for related models, however, so I’ve opted to use the nested method.

What are your thoughts?

--

--

Sascha Kala

Software engineer and they/them tech femme; artist // digital creator