Building Awesome Rails APIs: Part 1

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

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


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'

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

class API::PeopleController < ApplicationController


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


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

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


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

Unfortunately, now out URls look like: We can remove the duplication of “api” by setting a blank path:

namespace :api, :path => "", :constraints => {:subdomain => "api"} do
  namespace :v1 do
    resources :people

Now our URls look like: or just 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 }


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

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.

Photo of Daniel Morrison

Daniel founded Collective Idea in 2005 to put a name to his growing and already full-time freelance work. He works hard writing code, teaching, and mentoring.


    June 14, 2013 at 7:44 AM

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

  2. June 15, 2013 at 4:14 AM

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

  3. June 19, 2013 at 17:11 PM

    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.

  4. June 20, 2013 at 13:40 PM

    Looking forward to part 2 :)

  5. June 20, 2013 at 13:56 PM

    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.

  6. June 20, 2013 at 21:46 PM

    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?

  7. June 21, 2013 at 12:56 PM

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

    Uh huh. :)

  8. June 24, 2013 at 12:16 PM

    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
    June 24, 2013 at 20:56 PM

    Wishing for part 2!!! Great post…

  10. June 28, 2013 at 19:19 PM

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

  11. July 16, 2013 at 13:44 PM

    Thanks! Looking forward to part 2. I’m curious about how to handle the views (jbuilder or rable?) or do you use custom serializations ( 

    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.

  12. July 19, 2013 at 19:52 PM

    Great post,  looking forward to part 2

    Craig Walsh
    September 10, 2013 at 13:31 PM

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

  14. September 11, 2013 at 8:15 AM

    Great post! Thanks for sharing :)

  15. October 30, 2013 at 21:31 PM

    Many thanks.

  16. November 06, 2013 at 21:10 PM

    Thanks, this actually helped me a lot :-)

  17. November 13, 2013 at 20:28 PM

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

  18. November 17, 2013 at 17:21 PM

    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.


    November 21, 2013 at 19:02 PM

    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. :)

    November 21, 2013 at 19:03 PM

    By the way,

    the gem is named ‘api_explorer’ and the repo is in

    December 13, 2013 at 0:04 AM

    great post ! What is you opinion about using JBuilder?

    December 19, 2013 at 9:43 AM

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

    December 31, 2013 at 6:36 AM

    not working for rails it a rises routing error friend

  24. January 08, 2014 at 16:04 PM

    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
    February 22, 2014 at 7:17 AM

    Useful post. Thank you. Looking Forward…

  26. March 04, 2014 at 21:22 PM

    Thanks for sharing the post, it is very useful.

    March 20, 2014 at 1:21 AM

    great post. waiting for part 2. 

  28. March 28, 2014 at 22:58 PM

    When is Part 2…. it has been a while?

  29. April 04, 2014 at 21:50 PM

    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
    April 04, 2014 at 22:36 PM

    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.

  31. April 07, 2014 at 14:36 PM

    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 ?

  32. June 05, 2014 at 12:49 PM

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

    June 23, 2014 at 7:37 AM

    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
    July 09, 2014 at 11:10 AM

    Nice One!!

    August 04, 2014 at 9:33 AM

    @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
    August 18, 2014 at 22:49 PM

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

    February 13, 2015 at 6:55 AM

    Great post

  38. May 19, 2015 at 13:44 PM

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

    John Robert Peters, Jr.
    May 22, 2015 at 8:45 AM

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

    June 20, 2017 at 14:34 PM

    Thanks, helpful post!