Collective Idea

Collective Idea Logo

Daniel Morrison

Building Awesome Rails APIs: Part 1

By Daniel Morrison on June 13, 2013 in api, best practices, and rails

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 controversial1, 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 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 level2. 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.

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.


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

Dead Man's Snitch

By Daniel Morrison on June 13, 2013 in api, best practices, and rails

31 Comments

  1. Lee

    Lee June 14, 2013

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

    Jaymie Jones June 15, 2013 http://pixelstack.com

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

  3. Alexandr Korsak

    Alexandr Korsak June 19, 2013 http://oivoodoo.tumblr.com

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

    Jonas June 20, 2013 http://autouncle.com

    Looking forward to part 2 :)

  5. Daniel Morrison

    Daniel Morrison June 20, 2013 http://collectiveidea.com/

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

    Phill Sparks June 20, 2013 http://phills.me.uk/

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

    Zach Moazeni June 21, 2013 http://connectionrequired.com

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

    Uh huh. :)

  8. mike banks

    mike banks June 24, 2013 http://hypnoticrhythm.org

    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! 

  9. Jon Gillies

    Jon Gillies June 24, 2013

    Wishing for part 2!!! Great post…

  10. Skip Baney

    Skip Baney June 28, 2013 http://voxmedia.com

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

    Andre July 16, 2013 http://panofy.com

    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.

  12. Cheyne

    Cheyne July 19, 2013 http://https://cheynewallace.com

    Great post,  looking forward to part 2

  13. Craig Walsh

    Craig Walsh September 10, 2013

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

  14. Kirsty

    Kirsty September 11, 2013 http://www.kirstywilliams.co.uk

    Great post! Thanks for sharing :)

  15. Keif

    Keif October 30, 2013 http://gnoshmeness.wordpress.com

    Many thanks.

  16. Fredrik Casserfelt

    Fredrik Casserfelt November 06, 2013 http://www.spotmonkey.se

    Thanks, this actually helped me a lot :-)

  17. Estevão

    Estevão November 13, 2013 http://neoron.io

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

  18. Alvin Crespo

    Alvin Crespo November 17, 2013 http://alvincrespo.com

    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!

  19. Anthony

    Anthony November 21, 2013

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

  20. Anthony

    Anthony November 21, 2013

    By the way,

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

  21. Alejandro

    Alejandro December 12, 2013

    great post ! What is you opinion about using JBuilder?

  22. Mark

    Mark December 19, 2013

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

  23. madhu

    madhu December 31, 2013

    not working for rails it a rises routing error friend

  24. Retrospective tool

    Retrospective tool January 08, 2014 http://retrospectus.com

    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

  25. Rakesh Haridas

    Rakesh Haridas February 22, 2014

    Useful post. Thank you. Looking Forward…

  26. Robin Solanki

    Robin Solanki March 04, 2014 http://www.iamrob.in

    Thanks for sharing the post, it is very useful.

  27. adi

    adi March 19, 2014 http://adi89.github.io

    great post. waiting for part 2. 

  28. chad taylor

    chad taylor March 28, 2014 http://www.linguabee.com

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

  29. Martin Streicher

    Martin Streicher April 04, 2014 http://locomotivellc.com

    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. 

  30. Steve Richert

    Steve Richert April 04, 2014

    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

  31. chebyte

    chebyte April 07, 2014 http://www.chebyte.com

    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 ?

Post a Comment

Contact Us

Find us on Google Maps
Collective Idea
44 East 8th Street, Suite 410
Holland, Michigan 49423 USA 42.790334-86.105251

Follow us on the Interwebs

We are currently available for medium and long term projects. Please get in touch if we can be of service.