Life in the Fast-Json Lane: Working with JSON:API Serializer
Tags: json, api, ruby
Published: 2021-09-22 02:54:08

Last week, I covered tricks and tips with handling forms and updating state, as well as sending the data through fetch requests to the backend. Since then, I have been working endlessly on fetching data from the backend, which included building relationships between my various models and sending the data through a Json Serializer made by Netflix, [Fast_JSONAPI Serializer](https://github.com/Netflix/fast_jsonapi) (now just [JSONAPI Serializer](https://github.com/jsonapi-serializer/jsonapi-serializer)). I learned some new things while working with the serializer and felt I could impart my wisdom this week.

My application’s database is made up of multiple tables/models that are all connected through belongs_to and has_many relationships. Employees have profiles and previous work experience, and employers have job postings, which are all connected through their job applications. On previous projects, I would make too many fetch requests to acquire necessary data, for example one request for an employer and another for associated applicants. I was slowly realizing that if an employer had a lot of job postings or applicants, the data might take a long time to load.

I had some experience building serializers in the past, which helped define the data in a readable json format. This included iterating through associated data in an attempt to minimize fetch requests. The reducer would receive the results and I would iterate through the nested data to correctly update the state.

```
class RecordSerializer
    include FastJsonapi::ObjectSerializer
    attributes :id, :date, :daily_allowance, :daily_total, :user_id
    has_many :days
    attribute :days do |record|
        days = Day.where(record_id: record.id)
        recs = {}
        days.each do |day|
            recs[:id] = day.id
            recs[:item_name] = day.item_name
            recs[:item_calories] = day.item_calories
        end
    end
end
```

It worked fine in the past, but I knew there had to be a more efficient way to deliver the data without it being so nested in the results. Upon reading the instructions for [JSONAPI](https://github.com/jsonapi-serializer/jsonapi-serializer), I found that it wasn’t necessary to define an iterator. By setting up a serializer similar to a model with belongs_to and has_many attributes, I could simply include the profiles and work history when an employee is initially fetched.

```
class EmployeeSerializer
    include JSONAPI::ObjectSerializer
    attributes :id, :email, :name
    has_one :profile
    has_many :work_histories, through: :profile
    has_many :applicants
    has_many :jobs, through: :applicants
end
```
```
render json: EmployeeSerializer.new(@employees, include: [:profile, :work_histories])
```

Things started to become a little bit clearer, until I started attempting to pull large sets of data that were connected to jobs. When an Employer signs in, I envisioned jobs data associated with the employer would be delivered as well. Then I expanded to include potential candidates (connected through applicants join-table) which also included the profile information and previous work experience. My reducer was becoming larger and filled with too much code, and I could visually see the long logs to fetch the jobs.

I went back to the read me portion of [JSONAPI](https://github.com/jsonapi-serializer/jsonapi-serializer) and learned that there was an alternate way to deliver associated records. 

```
class JobSerializer
    include JSONAPI::ObjectSerializer
    belongs_to :employer
    has_many :applicants
    has_many :employees, through: :applicants
    has_many :profiles, through: :employees
    attributes :id, :employer_id, :status, :title, :city, :state, :jobtype, :schedule,
    :skills, :certificates, :description, :employees, :profiles
end
```

Since both employees and their profiles are already related to jobs, they can be inserted as attributes for JobSerializer. This will deliver all associated employee information already defined in the EmployeeSerializer, along with profiles defined in the ProfileSerializer. With a single fetch request, I have all the necessary data to display an employer’s jobs postings and potential candidates information, rather than fetching employees later on. Originally, I was defining the parameter through object_method_name, but I realized (stumbled upon) that it was only necessary if I wanted to assign the parameter a name different from its given name.

I might have spent a little too much time refactoring my code instead of building on features for my application, but my code is much cleaner and works very well to boot. The real test will be when I have copious amounts of records in my database and how long it takes to load it all on this single fetch request. I’m also attempting to learn how to define related links in the Serializer that [JSONAPI](https://github.com/jsonapi-serializer/jsonapi-serializer) says would be helpful in gathering data in later requests. I will attempt it and see how well it turns out. Until next time!