Building a JSON API with Rails – Part 2: Serialization


Foreword:

This series has been rewritten as of November 11, 2016 based on the new API features in Rails 5. Formerly, this post covered the use of the rails-api gem, which has now been merged into core Rails 5.


Table of Contents


Welcome to part 2 of our API building adventure. If you haven’t read Part 1: Getting Started yet, then I highly recommend you go through that post real quick to make sure we’re all on the same page. We’ll be continuing to develop on our Blog API which uses three relational tables: User, Post, and Comment. In the last post, we focused on setting up a basic JSON API using Rails, preparing our database, and reviewing how to issue requests to that API. Today, we’re going to take the power of our API to another level with serialization.

Serialization

So what exactly does it mean to serialize our API? Currently when we make a GET request to one of our API endpoints (like /users/1 or /posts/1), we get back all of that object’s attributes from the database record (or multiple objects’ attributes, if querying an index action). This might seem okay at first, but let me give you an example of how this is undesirable. When we query /users/1, we will get all of that user’s data – including the unencrypted password. That’s a huge security flaw. Additionally, what if we wanted to query that same endpoint and return each of that user’s posts in addition to their user attributes? We can’t do that right now. That’s where serialization will help us.

We employ serialization in APIs to properly handle the response from our GET requests to the API and format the data exactly how we want. Could we handle this directly in the controllers? Yes, but that gets real messy real quick, and it all goes back to our concept of SOA – Service Oriented Architecture. Controllers are meant to handle the business logic of our app, and not focus on formatting a response – but handling response data is exactly what serializers are for, so let’s use them and keep our API clean and modular!

Setting It Up

There are different ways we can apply serialization in Rails, and all of them involve gems. The three most common serialization gems are:

Each of these gems are very well supported and have hundreds of forks on GitHub, but I prefer to use ActiveModelSerializers (AMS) – predominantly because right out of the box it plays very nicely with Ember.js via Ember Data’s ActiveModel Adapter. If you’ve used Ember, then you know it’s very powerful, but you have to play by its rules – and using AMS allows you to do that. If you don’t use Ember, AMS is still a wonderful serializer and is very Rails-esque in syntax.

Let’s install AMS by adding it to our Gemfile:

Then run a bundle install. AMS comes built-in with generators, so to create a serializer for our User resource for instance, we just run:

And that will create the following file:

Now, if you navigate to your /users URL, you should see JSON that looks like this:

This is different from what we’ve seen in two ways.

  1. We now have a root users key and are returning a JSON object instead of an array of JSON objects.
  2. We are only rendering the id, and no other data on the User objects. That means no more exposed passwords!

See how simple that was? This same serialization pattern will also carry over for all of your controller actions that handle GET requests that return JSON. Now go ahead and run the serializers for the remaining Post and Comment resources, and then we’ll get into some configuration:

Configuring the Serializers

We won’t go into full configuration options here, as you’re better off checking the AMS documentation for that, but we’ll go into the core options that will help you the most. If you want to return more model fields than just your ID, then you just need to add them to the attributes method call like so:

And now when you query your User endpoints, you’ll receive the email and created_at fields too – easy as pie! But that’s not all. Let’s say you wanted to query a User endpoint and return each of those user’s posts too. Well that’s easy, and here’s where you really see the Rails-y design of AMS:

And wallah! You are now returning each user’s posts when you query a user – and the JSON data for each post will also follow the configuration in the serializer created for the Post resource.

I just have one last serializer configuration I wanted to share. Occasionally, you may want to modify the data that you return in JSON, but because this specific alteration is only meant for serialization cases, you don’t want to dirty up the model files by creating a model method. AMS provides a solution for that. You can create a method inside your serializer and therein access the current object being serialized, and then call that method with the same syntax as if it were an attribute on that object. Doesn’t make sense? Take a look at this example:

Now our serializer would spit out a say_hello JSON key that would have as its value the word “Hello” followed by that user’s email address. You access the current serialized object via the ‘object’ variable inside of any method you define inside your serializer. Nifty, huh? Pro Tip: You can also add model methods into your attributes method call, and don’t have to redefine them in the serializer.

One last thing (didn’t I already say that above?): If you don’t like the JSON syntax of having a root key like user or whatever resource you’re querying, then you can go back to the old syntax we had where it strictly returns either just the JSON representation of the object (e.g. show), or an array of JSON objects (e.g. index). You just have to add a short method in your application_controller.rb to set it globally:

Wrap Up

That’s it for serializer configuration that we’re going to cover in this post, but there’s a lot of other neat options you can play with using AMS. As I mentioned earlier, I initially chose AMS over other serialization gems because of how nicely it plays with Ember.js, but it’s built to be completely agnostic of whatever front-end framework you use. For example, I’m currently working on a large Angular.js app, and AMS is still my chosen serialization gem of choice because it does everything I need it to (and beautifully at that).

We’ve now covered the actual building of an API and serializing our JSON response to format it exactly like we want. Technically this is all you need in your server-side API, but I want to review one more very important topic: Authentication. After all, there’s a big chance that you plan to build an API that houses personal data that you don’t want everybody in the whole world to be able to query. In the next post, we’ll cover how to authenticate your requests so that only you can access your personal data, and no one else can!

Continue on in part 3 of this series: Authentication Strategies.

A Genuine Request

Hello there - thank you for reading my post! If you enjoyed it, I'd love it if you considered supporting me. I'm not asking for money or any of that "buy me a beer" crap; I would just love it if you would consider downloading (and using) the Brave browser using my referral code!

The Brave Browser Brave has a focus on security and privacy - and is hardcore against ads. That means that sites like mine wouldn't generate any revenue if you viewed them in Brave - but that's okay; I only make about $10 / month with ads, and that goes right to hosting. If you use my referral code to download Brave and use it for a bit though, I get about $5 with Brave's currency - Basic Attention Token, or BAT for short.

Whether you consider using it or not, I'm just glad you read this post. Thanks again!

Referral Code: https://brave.com/the124