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
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.
[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
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..
I concur on Auth systems, it would be great to know what your thoughts are on what you would deploy, etc.
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.
Looking forward to part 2 :)
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.
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?
> I’m not trying to take a side here, just showing an easy way to do it via URLs.
Uh huh. :)
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!
Wishing for part 2!!! Great post…
> 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.
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.
Great post, looking forward to part 2
This really helped me starting a new project. Hopefully part two will be soon? ;-)
Great post! Thanks for sharing :)
Many thanks.
Thanks, this actually helped me a lot :-)
Thanks, great article!
Looking forward to the next part. :-)
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!
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. :)
By the way,
the gem is named ‘api_explorer’ and the repo is in http://www.github.com/toptierlabs/api_explorer
great post ! What is you opinion about using JBuilder?
So that’s the trick. Anyways, great post. Can’t wait for part 2!
not working for rails it a rises routing error friend
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
Useful post. Thank you. Looking Forward…
Thanks for sharing the post, it is very useful.
great post. waiting for part 2.
When is Part 2…. it has been a while?
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.
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
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 ?
Excellent article. I’d be very interested to hear how you deal with JSONP.
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:
peopleNice One!!
@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.
Any plans for part 2? Would be very interested to see how you handle security and authentication.
Great post
Als oprichter van Nutrogenics bied ik u via deze firma supplementen aan onder de
merknaam WHC.
Is there a part 2 to this program or are you done?
Thanks, helpful post!