These are really exciting times for web developers. New JavaScript frameworks emerge constantly, completely changing the way we think about building web applications. With libraries such as AngularJS, Ember.js or Backbone.js, the server is no longer responsible for generating complete pages. Its role is often reduced to serving as a backend for client-side application. This approach has many advantages. To name a few:

it allows better separation between presentation and business logic

better performance for the user - instead of reloading the whole page, only parts that need to change are replaced

reduced resources usage - fetching only small chunks of data instead of rendering whole pages, as well as pulling large parts of business logic into web browser, can be a huge relief for your web server and as a result - for your pocket

it can save you substantial amount of work if you’re creating multiple clients, for example website and native mobile clients for iOS, Android etc. A good, well-defined API can be reused for all these purposes.

You might have doubts if large, full-featured framework like Ruby on Rails is a right tool for building such backend. Indeed, it might look like an overkill. However, I believe it is not. With just a little bit of configuration and some useful gems, we can disable the parts that we don’t need and still use all the Rails’ goodness we’re used to. And, while it might not be obvious at first sight, many Rails features are applicable to API applications:

routing - resourceful routes lie at the heart of a good REST API. Rails router makes it super easy co create them effortlessly

easy testing - testing is vital part of software development, and API applications are no different.

rich library of third-party plugins - no matter what functionality your application needs, chances are that someone already implemented it

convenient defaults for development mode

logging

and much more

In this article we’ll have a look at creating an API using Ruby on Rails. We’ll try to cover some of the most common issues one can encounter when developing such application. For illustrative purposes we’ll use a sample application, which allows tracking projects user participates in.

Getting Started

When creating an API application we don’t need all the features that Rails provides out of the box. For example, we’re not interested in generating templates on the server. Picking the features needed for building an API is trivial when using Rails::API gem.

We start by adding appropriate entry to our Gemfile:

gem 'rails-api'

and running bundle install . Next, we want our controllers to inherit from ActionController::API . To achieve this, let’s edit app/controllers/application_controller.rb file. Change

class ApplicationController < ActionController::Base protect_from_forgery ... end

to

class ApplicationController < ActionController::API ... end

As you might have noticed, we also removed protect_from_forgery call.

Alternatively, if you’re creating your application from scratch, you can install rails-api as a standalone gem

$ gem install rails-api

and generate new project using

$ rails-api new my_api

Rails::API includes sensible set of middlewares enabled by default. It’s possible to add the ones that are inactive. For example to add Session Management, you simply need to add following line in config:

config.middleware.use Rack::SessionManagement

Middlewares can be disabled with following code:

config.middleware.delete Rack::SessionManagement

Full documentation for Rails::API can be found here.

Versioning

To be of any value, API must remain stable and consistent. If you introduce any changes to the routing, parameters passed to the API or response format, clients using the API might break. The above statement is especially true for publicly available APIs, which can be used by large number of third-party clients. On the other hand, evolution of software is completely natural and inevitable. How can we reconcile these contradictions? API versioning to the rescue! Thanks to versioning, you can release new, improved API, without cutting off clients using the old one. Let’s set up routes for our versioned API: config/routes.rb

MyApp::Application.routes.draw do namespace :api do namespace :v1 do resources :user ... end end end

Next, we need to create api/v1 directory under app/controllers and put our controllers there: app/controllers/api/v1/users_controller.rb

module Api module V1 class UsersController < ApplicationController ... end end end

If at some point in the future we decide to introduce new version of our API, we can simply create new namespace for it, like this:

MyApp::Application.routes.draw do namespace :api do namespace :v1 do resources :user ... end namespace :v2 do resources :user ... end end end

and create controllers structure analogous to the existing one.

Rendering Response

There are many formats, in which communication with the API can be performed. The most popular choices are XML and JSON. For our sample application we will use the latter exclusively. JSON, or JavaScript Object Notation, has its origins in JavaScript, but is widely supported by other programming languages, including Ruby. JSON’s main advantage over XML is simpler syntax, which makes it easier to read by human and faster(both in parsing and - due to smaller size - transmission over the Internet). Thanks to built-in support for JSON, creating response in this format in Ruby on Rails is a breeze:

class ProjectsController < ApplicationController ... def index ... render json: {message: 'Resource not found'} end ... end

This way works perfectly fine for simple responses. However, if there’s more data you would like to return, you controller might get pretty fat. For these cases, we can use one of available DSLs. The one I find most convenient is Jbuilder, enabled by default in Rails 4. If you’d like to use it with older version of Rails, you might need to uncomment it in the Gemfile (or add it altogether, for really old versions):

# To use Jbuilder templates for JSON gem 'jbuilder'

It helps you by adding support for loops and conditional statements, as well as partials. Let’s see all that in action! Jbuilder files are treated like a regular template files: you put them in app/views directory. For example, let’s have a look at template for single project: app/views/api/v1/projects/show.json.jbuilder

project ||= @project json.id project['id'] json.name project['name'] json.source_name project['source_name'] json.source_identifier project['source_identifier'] json.task_count project['task_count'] if project.class == Hash json.active Project.find(project['id']).active_for_user?(@api_key.user) else json.active project.active_for_user?(@api_key.user) end if project.class == ActiveRecord::Base && !project.persisted? && !project.valid? json.errors project.errors.messages end

Now, let’s see how we can use this template as a partial, to return all projects user is assigned to: app/views/api/v1/projects/index.json.jbuilder

json.projects @projects, partial: 'api/v1/projects/show', as: :project json.total_count @projects.respond_to?(:total_entries) ? @projects.total_entries : @projects.to_a.count

Above template will result in following JSON:

{ "projects": [ { "project": { "id":17, "name":"Death Star contruction", "source_name":"Pivotal Tracker", "source_identifier":"796315", "task_count":188 } }, { "project": { "id":77, "namespaceme":"Get The Ring to Mordor", "source_name":"Pivotal Tracker", "source_identifiere_identifier":"123456", "task_count":4 } } ], "total_entriesl_count":2 }

Of course, Jbuilder is not the only option you have. I suggest also having a look at RABL.

Response from an API is not complete without correct HTTP response code. To set status code in you controller, just pass :status key to render method, like this:

render json: {message: 'Resource not found'}, status: 404

You can also use symbols instead of numbers:

render json: {message: 'Resource not found'}, status: :not_found

Full list of status codes can be found here, but you’re most likely to use only limited subset of them:

200 - :ok

204 - :no_content

400 - :bad_request

403 - :forbidden

401 - :unauthorized

404 - :not_found

410 - :gone

422 - :unprocessable_entity

500 - :internal_server_error

One last improvement to the way we handle response formats is setting JSON as the default. We can do so in config/routes.rb file:

namespace :api, defaults: {format: 'json'} do namespace :v1 do ... end end

This way, when sending request to

http://www.example.com/api/v1/projects.json

we can omit the format:

http://www.example.com/api/v1/projects

Authentication

An API is a gateway to your application. In most cases you don’t want to allow everyone to access, edit and remove data. You need to make sure that only authorized users will be able to alter it. One way to secure your API is by Access Tokens. We’ll create a separate model, called ApiKey , to store token. The token itself will consist of 32 hexadecimal characters. Let’s have a look at ApiKey class:

class ApiKey < ActiveRecord::Base attr_accessible :user, :token belongs_to :user before_create :generate_token private def generate_token begin self.token = SecureRandom.hex.to_s end while self.class.exists?(token: token) end end

The class is very simple. It has a callback method generate_token , which creates unique string of hexadecimal characters when the class is instantiated. Next, let’s make sure that every user will be assigned his api key upon registration

app/models/user.rb

class User < ActiveRecord::Base ... has_one :api_key, dependent: :destroy after_create :create_api_key ... private def create_api_key ApiKey.create :user => self end end

Now, when we receive a request, we need to check if it contains correct token and find the current user based on it. We do it in the Application controller app/controllers/appliation_controller.rb

include ActionController::HttpAuthentication::Token::ControllerMethods include ActionController::MimeResponds class ApplicationController < ActionController::API private def restrict_access unless restrict_access_by_params || restrict_access_by_header render json: {message: 'Invalid API Token'}, status: 401 return end @current_user = @api_key.user if @api_key end def restrict_access_by_header return true if @api_key authenticate_with_http_token do |token| @api_key = ApiKey.find_by_token(token) end end def restrict_access_by_params return true if @api_key @api_key = ApiKey.find_by_token(params[:token]) end end

We support passing acces token both as a header and parameters. To support header authentication, we need to include ActionController::HttpAuthentication::Token::ControllerMethods module, which is disabled by Rails::API by default. Now, we need to make sure that the token will be checked with each call to the API. We’ll add before_filter to our controllers:

before_filter :restrict_access

Testing

When testing an API, you should test both response body and HTTP status codes. An example of test that checks both: spec/api/tasks_api_spec.rb

require 'spec_helper' require 'api/api_helper' require 'fakeweb' require 'timecop' describe 'Tasks API' do before :each do FactoryGirl.create :integration FactoryGirl.create :project FactoryGirl.create :task Project.last.integrations << Integration.last end # GET /tasks/:id it 'should return a single task' do api_get "tasks/#{Task.last.id}", {token: Integration.last.user.api_key.token} response.status.should == 200 project = JSON.parse(response.body) project['id'].should == Task.last.id project['project_id'].should == Task.last.project_id project['source_name'].should == Task.last.source_name project['source_identifier'].should == Task.last.source_identifier project['current_state'].should == Task.last.current_state project['story_type'].should == Task.last.story_type project['current_task'].should == Task.last.current_task project['name'].should == Task.last.name end ...

Above test uses helper method api_get , which constructs a request and returns parsed response body: spec/api/api_helper.rb

def api_get action, params={}, version="1" get "/api/v#{version}/#{action}", params JSON.parse(response.body) rescue {} end def api_post action, params={}, version="1" post "/api/v#{version}/#{action}", params JSON.parse(response.body) rescue {} end def api_delete action, params={}, version="1" delete "/api/v#{version}/#{action}", params JSON.parse(response.body) rescue {} end def api_put action, params={}, version="1" put "/api/v#{version}/#{action}", params JSON.parse(response.body) rescue {} end

Documenting with RDoc

If you want your API to be publicly available, it’s important to document it well. There’s no single recipe for writing a good documentation, but you should consider putting following info in it:

description, saying in short what’s the purpose of given method

list of required and optional parameters that can be passed to the method

possible response codes

format of response

sample calls

To generate the documentation we used RDoc, which allows you to create documentation based on the source code.

module Api module V1 class ProjectsController < ApplicationController ... ## # Returns a list of recent projects for a given user # # GET /api/v1/projects/recent # # params: # token - API token # # = Examples # # resp = conn.get("/api/v1/projects/recent", "token" => "dcbb7b36acd4438d07abafb8e28605a4") # # resp.status # => 200 # # resp.body # => [{"project": {"id":1, "name": "Sample project", "source_name": "Pivotal Tracker", "source_identifier": "123456", "task_count": "2", "active": true}}, # {"project": {"id":3, "name": "Some random name" "source_name": "GitHub", "source_identifier": "42", "task_count": "0", "active": true}}] # # resp = conn.get("/api/v1/projects/recent", "token" => "dcbb7b36acd4438d07abafb8e28605a4") # # resp.status # => 404 # # resp.body # => {"message": "Resource not found"} # def recent @projects = Project.recent(@current_user) if @projects[0].present? render 'index' else render json: {message: 'Resource not found'}, status: 404 end end ... end ebd end

The syntax for formatting your comments is very simple. Here are some basic rules, that you’ll find most useful:

lines starting with = are headers. The number of = characters defines the level of heading, so = Header will result in following HTML: <h1>Header</h1> , while == Header will give you <h3>Header</h3>

hyphen and asterisk are items of unordered list

digit or a letter followed by a dot are itams of ordered list

for labelled list, wrap labels in square brackets or append two colons to them [label] description another:: description 2

to alter text style, use bold, italics or +monospace+

any lines starting with larger indentation than the current margin are treated as verbatim text. It’s great for embedding code snippets with syntax highlighting inside the documentation

It’s also possible to use different markup format, than the default RDoc::Markup . RDoc has built in support for markdown, rd and tomdoc.

More in-depth reference for Rdoc syntax, as well as another markup formats can be found in project documentation

To generate documentation, run rdoc command in project main directory. It will store html files containing newly created documentation inside doc directory. You can also define index page for a documentation. In our case, the command to generate documentation will be

rdoc --main README.md

Summary

We covered basic steps required to build your own REST API with Ruby on Rails. It was by no means comprehensive guide, but it should be enough to get you started on yout way to mastering the art of building great APIs.