Using Rails serializers to render data through a has_many relationship.
Tags: rails, ruby, react, javascript
Published: 2023-07-04 18:12:05

Have you ever had the need to not only fetch a single models data but the data included in that models relationships with other models? 

Using Rails as a backend API server we can hone in on the data we want to render and send to our front end application. Say we have the following relationships:
**USER:**
```
  has_many :comments
    has_many :posted_pets, :class_name => "Pet", :foreign_key => "user_id"
    has_many :pets, through: :comments

```
**PET:**
 

```
  belongs_to :user
    has_many :comments, dependent: :delete_all
    has_many :users, through: :comments
```
**COMMENTS:**

```
    belongs_to :user
    belongs_to :pet
```

These relationships establish that a user has many posted pets using an alias and has many comments that belong to a pet. 

Our need at the front end side of the application is that we want to display a pet with their list of comments and an individual comment will display the comments users username like below:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9vn3krgj0alwaaihz4e.png)

Our first task is to render the pet model data, but we want to establish what we want to render to the front end of the application. To do so we use a serializer. To create a serializer using Rails we need to include the following in our terminal `rails g serializer pet`. You will notice that the name of the serializer "pet" is singular. This follows the proper naming convention to ensure this serializer is used as the default serializer for the "Pet" model. Lets take a look at the now generated serializer for "pet".


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rdbpdnlo06h7j1nep7q5.png)

The serializer allows us to control the attributes that will be rendered to the front end. Say we have a found address attribute that we do not want to display in the front end of the application. We can exclude this attribute in the serializer to keep the data from being rendered. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r71azolvryfsp4rwh7ka.png)

Now this serializer is being used when we fetch our Pet model data. Here is a glimpse of the controller rendering the data to the front end application. 

```
   def show
        pet = Pet.find_by(id: params[:id])
        if pet
            render json: pet,
        else
            render json: {error: "Pet not found"}, status: :not_found 
        end 
    end
```
We however are not done. Looking back at our front end image we can see that we require the comments data that is associated with the pet. We already established our relationships in our models but now we can utilize our serializer to include the associated data when we render. Like so:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mgvc3yrbjgxx0ocde3gc.png)

Now we get the pet data and all the comment data associated with the pet. We can stop here but lets say we want to also control the comment data we render to the front end. To do so we create another serializer for the comment model using our `rails g serializer comment`. Lets see how this looks below:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qfovdc4a6621ri41fvr2.png)


Utilizing this serializer we are only included each comments ":id" and ":body" when rendering the data. No additional syntax is needed in our pet controller:

```
   def show
        pet = Pet.find_by(id: params[:id])
        if pet
            render json: pet,
        else
            render json: {error: "Pet not found"}, status: :not_found 
        end 
    end
```

We have our pet data and our comment data, but now we need the user that posted the comment's username. To do this we have to include more syntax in our controller because AMS or active model serializer is not able to render deeply nested data. 

A simple fix is to "include:" the data in our pet controller like so:

```

    def show
        pet = Pet.find_by(id: params[:id])
        if pet
            render json: pet, include: ['comments', 'comments.user']
        else
            render json: {error: "Pet not found"}, status: :not_found 
        end 
    end
```
Establishing the relationships and utilizing AMS we can finally render the following data to our front end:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lxozig5uz3euiba6xkn2.png)

What an amazing functionality! I hope this helped you learn more about AMS.