DEV Community

Cindy
Cindy

Posted on

Rendering JSON in a Rails API

To go over rendering JSON in a Rails API, I will use an example.

Example:

Models: doctor, patient, appointment
Relationships:

  • Doctor has many patients
  • Doctor has many appointments, through patients
  • Patient has many doctors
  • Patient has many appointments, through doctors
  • Appointment belongs to a doctor and belongs to a patient
# app/controllers/appointments_controller.rb

def index
  appointments = Appointment.all
  render json: appointments.to_json
end

def show
  appointment = Appointment.find_by(id: params[:id])
  render json: appointment.to_json
end

In this example, we have all of Appointment model's instances / objects assigned to the local variable, appointments. By using render json:, we are converting all the model instances into JSON. to_json method can add tacked on, but it is optional, as it will be called implicitly, thanks to Rails doing work behind the scenes.

# config/routes.rb

Rails.application.routes.draw do
    get '/appointments' => 'appointments#index'
    get '/appointments' => 'appointments#index'
end

Once you have the routes, controller, and models set up, you can start the Rails server to view your appointments.

# '/appointments/'
[
    {
        id: 1,
        doctor_id: 1,
        patient_id: 1
        },
    {
        id: 2,
        doctor_id: 2,
        patient_id: 2
    }
]

Adding relationships

So far we can only see the appointments information, which is a bunch of ids. What if we wanted to see an appointment's doctor and patient information? We can add options, such as include.

# app/controllers/appointments_controller.rb

def index
  appointments = Appointment.all
  render json: appointments.to_json(include: [:doctor, :patient])
end

def show
  appointment = Appointment.find_by(id: params[:id])
  render json: appointment.to_json(include: [:doctor, :patient])
end
[{
    id: 1,
    doctor_id: 1,
    patient_id: 1
    doctor: {
        id: 1,
        name: "Bob",
        created_at: "2020-04-26T21:56:50.874Z",
        updated_at: "2020-04-26T21:56:50.874Z"
    },
    patient: {
        id: 1,
        name: "Fred",
        created_at: "2020-04-26T21:56:50.924Z",
        updated_at: "2020-04-26T21:56:50.924Z"
    }
}]

We now have both the doctor's and patient's information, but what if we don't want to show certain attributes, like created_at and updated_at?
We could use the option, only:

# app/controllers/appointments_controller.rb

def show
  appointment = Appointment.find_by(id: params[:id])
  render json: appointment.to_json(
        :include => {
          :doctor => {only: [:name]}, 
          :patient => {only: [:name]}
        })
end

However, it is best practice to try to keep most of our logic out of our controllers. So, we can move this logic of arranging our JSON data the way we want it to a serializer class.

What is serialization?

Serialization is the process of translating data structures or object state into a format that can be stored or transmitted and reconstructed later (Wikipedia).

What is JSON serialization?

JSON is a data format that encodes objects in a string. JSON serialization is the process of transforming an object, in our case, a hash because we are writing Ruby, into that JSON string.

Generate serializer classes using Fast JSON API

Fast JSON API is a gem you can install in your project. It is one of many gems that you can use. Fast JSON API gives us a new rails generator, serializer, which allows you to quickly create a serializer class. You will need a serializer class for each model, for which you have data you want to serialize.

Running rails g serializer Appointment will create a serializers folder within app folder and then create a file called appointment_serializer.rb. You will need to add the relationships to AppointmentSerializer, just like you would in the models files.

# app/serializers/appointment_serializer.rb
class AppointmentSerializer
  include FastJsonapi::ObjectSerializer
  belongs_to :doctor
  belongs_to :patient
end 

Do the same thing for doctor and patient serializer. We need a serializer for these models as well because we want to specify the attributes we want to show.

# app/serializers/doctor_serializer.rb
class DoctorSerializer
  include FastJsonapi::ObjectSerializer
  attributes :name
end 
# app/serializers/patient_serializer.rb
class PatientSerializer
  include FastJsonapi::ObjectSerializer
  attributes :name
end 

Then, in the controller:

def show
  appointment = Appointment.find(params[:id])
  options = {
    include: [:doctor, :patient]
  }
  render json: AppointmentSerializer.new(appointment, options)
end

Notice AppointmentSerializer.new(). In order to start using the serializer, we have to create a new instance of the AppointmentSerializer class and then pass in the variable to turn it into JSON. We will also pass in the options parameter to the serializer to include the doctor and patient objects and their attributes. We used this for the show action, but this can be used in any of the controller actions.

# RESULT:

{
  "data": {
    "id": "2",
    "type": "appointment"
    },
    "relationships": {
      "doctor": {
        "data": {
          "id": "2",
          "type": "doctor"
        }
      },
      "patient": {
        "data": {
          "id": "2",
          "type": "patient"
        }
      }
    }
  },
  "included": [{
      "id": "2",
      "type": "doctor",
      "attributes": {
        "name": "Bob"
      }
    },
    {
      "id": "2",
      "type": "patient",
      "attributes": {
        "name": "Fred"
      }
    }
  ]
}

This is the way Fast JSON API structures the data. Other serializer gems will structure data differently.

Thanks for reading!

Top comments (0)