Rendering JSON in a Rails API
Published: 2020-04-30 19:11:05


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

``` ruby
# 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.

```ruby
# 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.


```json
# '/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`.

``` ruby
# 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
```
```json
[{
	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`:

``` ruby
# 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. 

```ruby
# 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.


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


Then, in the controller:


``` ruby
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.


``` json
# 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!