Most of us are aware that enabling API access for rails application is easy as Rails provides RESTful APIs by default. However, a little complexity arises when some responses are expected in xml format (maybe for some legacy system) and JSON format. Complexity increases when these API calls need authentication.
The initial obvious thought that comes to ones mind is to create different views for each format and add respond_to block in controller. No! You don’t have to do this.
Rabl
Rabl is a gem which comes in handy when you want to represent the response in both json and xml using a single template. These are the only 2 formats currently supported.
We can enable API for an existing application or new application – the steps are same! Lets see how we
- enable APIs without authentication.
- enable APIs with authentication.
Enabling API access without authentication
- Add ‘rabl’ entry to your Gemfile
- You might need to add json/xml parser gem to your Gemfile if don’t have them already
- Execute ‘bundle update’
Now, lets enable json & xml api response for https://localhost/tasks for getting list of tasks. Create the content file
#app/views/tasks/index.rabl collection @tasks attributes :id, :name, :priority, :complete_at, :task_type
In the above code we only mention the attributes that are need to be rendered. There is no need to change tasks_controller code if you haven’t used respond_to for specifying the supported format. Otherwise, you need insert the following code inside the respond_to block of index method of tasks_controller
#app/controllers/tasks_controller.rb#index
def index
# some code
respond_to do |format|
format.json{}
fromat.xml{}
end
end
All set! Now, lets now test the responses for json and xml format by using curl:
curl -v -H "Accept: application/json" -H "Content-type: application/json" https://localhost/tasks.json -k
[{"task":{"name":"Inform users", "task_type":"email", "priority":1, "id":2, "completed_at":null}}, {"task": {"name":"Update Bank or Building Society", "task_type":"document", "priority":1, "id":3, "completed_at":null}}]
curl -v -H “Accept: application/xml” -H “Content-type: application/xml” https://localhost/tasks.xml -k
<?xml version="1.0" encoding="UTF-8"?>
<tasks type="array">
<task>
<task-type>email</task-type>
<name>Inform users</name>
<completed_at nil="true"></completed_at>
<id type="integer">2</id>
<priority type="integer">1</priority>
</task>
<task>
<task-type>document</task-type>
<name>Update Bank or Society</name>
<completed_at nil="true"></completed_at>
<id type="integer">3</id>
<priority type="integer">1</priority>
</task>
</tasks>
So with single rabl template we can serve two different formats.
Enabling API access with authentication
Let us consider devise as authentication mechanism. Enable token_authenticatable along with other required devise modules in user model and run migration
#app/models/users.rb devise :token_authenticatable
We also need to add a migration for this
#db/migrate/20111201074702_add_token_authenticatable_to_users.rb
class AddTokenAuthenticatableToUsers < ActiveRecord::Migration
def self.up
add_column :users, :authentication_token, :string
end
def self.down
remove_column :users, :authentication_token
end
end
Enable/uncomment the following line in devise configuration, :auth_token is the name of the parameter be passed in the user for authentication
config.token_authentication_key = :auth_token
For sign_in and sign_up, devise takes care of most of the things. But if you want authentication_token as a response for sign_up/sign_in you need to do the following
- Add the fields in user model ‘attr_accessor’ to be shown/emitted in the response. For example, if you want email and authentication_token to be part of your response, we need to modify the model like this
#app/models/user.rb attr_accessible :email, :authentication_token
- Modify RegistrationsController create method. The following snippet shows us an example for json format sign_up request with api_key check
#app/controllers/registrations_controller#create
format.json {
if !params[:api_key].blank? and params[:api_key] == API_KEY
resource = warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new")
sign_in(resource_name, resource)
current_user.reset_authentication_token!
respond_with resource, :location => after_sign_in_path_for(resource)
else
render :json => {'errors'=>{'api_key' => 'Invalid'}}.to_json, :status => 401
end
}
format.any{super}
Suppose we have a pre-registered API_KEY as ‘mprmzayb’, we can use it to generate and retrieve an authentication token!
curl -v -H “Accept: application/json” -X POST -d ‘user”:{“email”:”email@email.com”, “password”:”password”, “password_confirmation”:”password”, “accept_terms”:1}}’ https://localhost/users.json?api_key=mprmzayb -k
{"user":{"accept_terms":true, "authentication_token":"VBXdiVeCDGQD3Rxtg9qp", "email":"email@email.com", "has_email_reminder":false}}
Now, if you need the task list API requires authentication (i.e. the authenticate_user! before_filter is enabled for index method of TasksController), we need to pass the authentication_token in the request from sign_in/sign_up request.
Assuming you have the index.rabl in place as shown above, lets test this out:
curl -v -H “Accept: application/json” -H “Content-type: application/json” https://localhost/tasks.json?auth_token=VBXdiVeCDGQD3Rxtg9qp -k
[{"task":{"name":"Inform users","task_type":"email","priority":1,"id":2,"completed_at":null}},{"task":{"name":"Update Bank or Building Society","task_type":"document","priority":1,"id":3,"completed_at":null}}]
Yeah! Devise automatically takes care of authentication, so in the following senerios appropriate error message is returned.
- If the user tries to access tasks after logout with old token
{"error":"Invalid authentication token."}
- If the user tries to access tasks without authentication token
{"error":"You need to sign in or register before continuing."}
Enjoy building your APIs in Rails!

Awesome tutorial, thanks for sharing! Glad to see you using RABL, hope its been helpful.
@nesquena, its been really helpful that is the trigger for this blog post. Thanks a lot for making this gem.
Nice post
Great tutorial, thanks a lot !
i think there is probably a mistake in your filename #app/views/tasks/index.rabl
shouldn’t it be #app/views/tasks/index.json.rabl ? or #app/views/tasks/index.xml.rabl
with your filename, the attributes line wasn’t taken into account
No, don’t have to create two rabl files for each format. This is the major point of this blog post, single rabl file (i.e., app/views/tasks/index.rabl) is sufficient enough to render both the formats based on request content-type. Remember, you need to have gems for json and xml.
Thanks for the tutorial. In terms of Enabling API access with authentication. Assuming i am using omniauth and devise and creating a provider similar to your other helpful post on ‘multiple applications with devise, omniauth and single sign on’, will i need to enable token_authenticatable in devise as describe in this article or it will be sufficient to just use the “uid” in the authentications model of the provider or perhaps the app_secret in the clients model of the provider.
Will use any of that nullify the need to enable token_authenticatable in devise. I following your tutorial on ‘multiple applications with devise, omniauth and single sign on’ but will want the provider to enable api access with authentication.
Thanks.
@Ed,
If you are using omniauth, once you are already signed in, you would have the oauth_token already. So, all API calls would get authenticated automatically if you append the omniauth oauth_token with your call.
I guess you just have to remember to use the parameter as ‘oauth_token’ and not ‘auth_token’ as in the API above. (as is with OAuth 2.0 RFC)
Ok thanks for the explanation.
Awesome ! Thanks a lot !
Good Tutorials..