Skip to Content Collective Idea Home

Building Awesome Rails APIs: Part 1

by Daniel Morrison

After teaching an Advanced Rails class last week, I realized that we use a lot of patterns internally to build great APIs in Rails that many people don’t know about. We didn’t invent most of them, but we use them with great success, so we need to start sharing.

Namespace your API

A really easy way to keep your API code clean is to namespace it. Give it its own controllers and routes. It is simple, and keeps your APIs independent from the rest of your controllers.

We start by adding this in our routes, assuming Person is the resource (model) we’re working with:

namespace :api do resources :people end

This now gives us the standard routes for people, but namespaced as /api/people. It will look for a PeopleController in app/controllers/api/people_controller.rb.

class Api::PeopleController < ApplicationController

end

This is great on its own, but we can even fix that ugly Api by using Rails’ built-in inflections. Over in config/initializers/inflections.rb you can use the commented out acronym lines to convert Api to API.

ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.acronym ‘API’ end

This is a small change, but now our controller can use the API module, which feels a bit better.

class API::PeopleController < ApplicationController

end

Now you’re free to start building out your controller in a nice, RESTful way.

Versioning

API versioning can be controversial[1], but if you want to add version info into your URLs, simply add another namespace to your routes:

namespace :api do namespace :v1 do resources :people end end

Now your version 1 API is at URLs starting with /api/v1/ and your controller would be in a v1 folder with the added v1 module.

class API::V1::PeopleController < ApplicationController

end

You could now support multiple versions at the same time, just make sure you have a very, very good test suite so you don’t break v1 when you go to v2.

API Subdomain

When possible, I recommend serving your API on a separate domain as it lets you load balance traffic at the DNS level[2]. A quick change to our routes file limits it to only being served on a specific subdomain.

namespace :api, :constraints => {:subdomain => “api”} do namespace :v1 do resources :people end end

Unfortunately, now out URls look like: http://api.example.com/api/v1/people. We can remove the duplication of “api” by setting a blank path:

namespace :api, :path => “”, :constraints => {:subdomain => “api”} do namespace :v1 do resources :people end end

Now our URls look like: http://api.example.com/v1/people or just http://api.example.com/people if you don’t use the version, it doesn’t interfere with your regular people routes, and it looks great.

Default to JSON

Most new APIs only need to serve JSON, yet it is common to see respond_to in API controllers:

class API::V1::PeopleController < ApplicationController

def index @people = Person.all respond_to do |format| format.json { render :json => @people } end end

end

We can drop the respond_to, but if you hit the url without .json you’ll see it thinking you’re using HTML in the logs.

Processing by API::V1::PeopleController#index as HTML

We can default the format in our routes to JSON:

namespace :api, :defaults => {:format => :json} do namespace :v1 do resources :people end end

Now we’ll see it knows we want JSON in the logs:

Processing by API::V1::PeopleController#index as JSON

This isn’t a necessary step, but can keep you in a JSON world. A great benefit is that you no longer have to specify the format in your test suite.

We have more tips for building awesome APIs that we’ll be sharing, but let us know in the comments if there are specific questions you have.


Check out our latest product, Dead Man’s Snitch for monitoring cron, heroku scheduler or any periodic task.

Dead Man's Snitch

[1] I’m not trying to take a side here, just showing an easy way to do it via URLs.

[2] If your API gets hammered, you can point the DNS for the API subdomain to another set of servers and scale it independently of your main app.

Comments

Lee
::

Great post TY.  One thing I personally would like to learn is how to secure your API. What Authentication & Authorization systems would you deploy etc…


Jaymie Jones
::

I concur on Auth systems, it would be great to know what your thoughts are on what you would deploy, etc.


Alexandr Korsak
::

Have you tried to use grape instead of rails actions?

In the latest rails version it’s easy to mount rack like applications. you can create api folder under app. and write your api code out of the rails.


Jonas
::

Looking forward to part 2 :)


Daniel Morrison
::

Alexandr Korsak: I haven’t used grape myself, but it seems fine. I’m not sure it would add a lot for most use cases, but the code looks good.


Phill Sparks
::

I like the idea of defaulting the format to JSON, however using this approach actually adds :format to the params too, this causes my routing tests to expect { :format => :json } (and so they all fail).  I wonder if there’s another way to do achieve the same effect?


Zach Moazeni
::

> I’m not trying to take a side here, just showing an easy way to do it via URLs.

Uh huh. :)


mike banks
::

In response to “I realized that we use a lot of patterns internally to build great APIs in Rails that many people don’t know about. We didn’t invent most of them, but we use them with great success, so we need to start sharing.” I once heard somebody say that it is not where you take things from, but where you take them to!


Jon Gillies
::

Wishing for part 2!!! Great post…


Skip Baney
::

>  I haven’t used grape myself, but it seems fine. I’m not sure it would add a lot for most use cases, but the code looks good.

It’s primary benefit (to me at least) is that by mounting a Grape API as a Rack app, you bypass most of the Rails response handing code. When I implemented an API for The Verge, I tested Grape against ActionController and Grape was significantly faster.


Andre
::

Thanks! Looking forward to part 2. I’m curious about how to handle the views (jbuilder or rable?) or do you use custom serializations (http://railscasts.com/episodes/409-active-model-serializers?view=asciicast).

I’ve took a look at grape, but sometimes your api is closely tied to your rails app. You can use the same relations, validations, custom attributes, etc as the rest of your rails app. This saves a lot of time. I agree that when writing a public api, grapes speed would help and allows to think more clearly about your api, making it more robust. But most of the time, (atleast for us) apis are used internally together with for instance backbone.js to create an async interactive experience for the user.


Cheyne
::

Great post,  looking forward to part 2


Craig Walsh
::

This really helped me starting a new project. Hopefully part two will be soon? ;-)


Kirsty
::

Great post! Thanks for sharing :)


Keif
::

Many thanks.


Fredrik Casserfelt
::

Thanks, this actually helped me a lot :-)


Estevão
::

Thanks, great article! Looking forward to the next part. :-)


Alvin Crespo
::

This is wonderful! Thanks for sharing this, totally going to follow your blog. The explanation and code examples in this tutorial got me going asap.

Cheers!


Anthony
::

I deployed a Rails engine (packed as a gem) that is really useful to test APIs on rails. You just have to mount the engine and go to the url that you specified, i.e. “localhost:3000/api_explorer” to see it. It’s a way of documenting an API also, reading the webservices specification from a file.

Any comments or help improving the api is welcome. :)


Anthony
::

By the way,

the gem is named ‘api_explorer’ and the repo is in http://www.github.com/toptierlabs/api_explorer


Alejandro
::

great post ! What is you opinion about using JBuilder?


Mark
::

So that’s the trick. Anyways, great post. Can’t wait for part 2!


madhu
::

not working for rails it a rises routing error friend


Retrospective tool
::

Really useful post, actually we use most of the patterns that you show additonallly we use Rabl and backbone.js in frontend, thanks for sharing Daniel


Rakesh Haridas
::

Useful post. Thank you. Looking Forward…


Robin Solanki
::

Thanks for sharing the post, it is very useful.


adi
::

great post. waiting for part 2.


chad taylor
::

When is Part 2… it has been a while?


Martin Streicher
::

What is your strategy when the API and the Web controllers essentially do the same thing, albeit one responding with HTML and the other with JSON? I can see the advantage of namespacing and versioning the API – but the rest are almost identical. I suppose I could have a superclass for both initially and over time diverge if needed.


Steve Richert
::

Martin: We use interactors to extract the behavior of a specific action into its own class, allowing that class to be reused across multiple controllers.

http://github.com/collectiveidea/interactor


chebyte
::

I have a question, some way to add cascade versioning support ? I mean when you send a request, the api first find in v2 then in v1 before throws error ?


Nicholas Johnson
::

Excellent article. I’d be very interested to hear how you deal with JSONP.


dharshan
::

Great article…learnt so many things from this. I think now in rails 4 we can directly render all the data using

people = Person.all render json: people


Rakesh Chand Rajwar
::

Nice One!!


Sunho
::

@chebyte I think you can extend V1 at V2, then you don’t have to make all of V1’s methods. But you have to make an empty controller instead.


Stijn Verrept
::

Any plans for part 2?  Would be very interested to see how you handle security and authentication.


Guru
::

Great post


supplementen lab test
::

Als oprichter van Nutrogenics bied ik u via deze firma supplementen aan onder de merknaam WHC.


John Robert Peters, Jr.
::

Is there a part 2 to this program or are you done?


Robert
::

Thanks, helpful post!