What is an API ?
An API defines an abstraction layer for software services and resources. The API describes the rules and the expected behavior so that the service or resource can be accessed as a black box, without a need to understand the details of how it is implemented.
So here are a few tips on how to build a Rails API :
An API defines an abstraction layer for software services and resources. The API describes the rules and the expected behavior so that the service or resource can be accessed as a black box, without a need to understand the details of how it is implemented.
So here are a few tips on how to build a Rails API :
Namespace
One of the best ways to maintain a good API code is to keep it independent from the rest of the controllers by moving it to app/controllers/api/ folder and name-spacing it.
config/routes.rb
namespace :api do
# End User APIs
resource :users, only: :index
end
You can also use comments to give more idea about who's going to use your api.
So here we go!
app/controllers/api/users_controller.rb
module Api
class UsersController < ApiController
#insert some code
end
end
Versioning
It is always advisable that a publicly available API should never be modified except for bug fixes because making any alteration would break client applications using APIs .So one of the best ways to tackle this would be introduce versioning.
config/routes.rb
namespace :api do
namespace :v1 do
resource :users, only: :index
end
end
app/controllers/api/v1/users_controller.rb
module Api
module v1
class UsersController < ApiController
#insert some code
end
end
end
Introducing a second versioning would be as simple as
namespace :api do
namespace :v1 do
resource :users, only: :index
end
namespace :v2 do
resource :users, only: :index
end
end
With comprehensive test coverage, backwards compatibility can be ensured.
Authentication
A base API controller is useful to handle authentication and extract common API functionality.So here EndUserApiController takes care of our authorisation logic and UsersController makes use of this base API to handle authentication.
module Api
module v1
class UsersController < EndUserApiController
#insert some code
end
end
end
app/controllers/api/v1/users_controller.rb
class EndUserApiController < ApplicationController
include Authorizable
before_filter :authorize_user!
def authorize_user!
#insert your authorisation logic here
end
end
Rendering Response
The most commonly used rendering responses are XML and JSON. JSON being more popular than XML because of its simpler syntax, faster both in parsing and due to small size transmission over net and is widely supported by most of the languages.
module Api
module v1
class UsersController < ApiController
def index
render json: { users: users }
end
private
def users
User.all.map do |user|
{ username: user.name, role: user.role }
end
end
end
end
HTTP Status Codes
Response from an API is only complete with correct HTTP response code.It is always advisable to
return status codes which make sense. You can also use symbols instead of numbers so instead of status: 200 you can status: :ok which makes the code more readable.
However there is an exception to this - If an AP user does not have access privileges instead of returning 401(unauthorised) you could trick the user by returning
404(not_found) so as to misguide the attacker about the existence of such an object.
So now we have something like this:
module Api
module v1
class UsersController < ApiController
def index
render json: { users: users }, status: :ok
end
private
def users
User.all.map do |user|
{ username: user.name, role: user.role }
end
end
end
end
Testing
Controller level tests are usually written to test an API wherein the response body is compared with the expected end result.This is however no different from our normal controller tests except that
we need to frame up the JSON response body before we do any comparison.
spec/controllers/api/users_controller_spec.rb
require 'spec_helper'
describe Api::UserController do
describe '#index' do
let(:token) { user.authentication_token }
let(:json) { JSON.parse(response.body) }
let!(:user) { FactoryGirl.create(:user) }
let!(:another_user) { FactoryGirl.create(:buyer_user) }
end
it 'returns a list of associated dealerships' do
get :index, auth_token: token, format: :json
expect(json['users']).to eql [
{"username": user.name, "role": user.role},
{"username": user.name, "role": user.role}
]
end
end
Documenting
Documenting the API is another important task.One should make sure the following points are included in your documentation
- a short one-liner description of the api
- list of required and optional parameters that can be passed to the method
- possible response codes and format of response
- sample calls
module Api
class UsersController < EndUserApiController
respond_to :json
##
# Get a listing of a users
#
# @resource /api/users
# @action GET
#
# @required [String] auth_token
#
# @example_request
# ```json
# {
# "auth_token": "ASZNfqL4kFFMezw6xTW5"
# }
# ```
#
# @example_response
# ```json
# {
# "users": [
# {"username": "Sithu", "role": "Technical Assistant"},
# {"username": "Kiran", "role": "Technical Head"}
# ]
# }
# ```
def index
render json: { users: users }
end
private
def users
User.all.map do |user|
{ username: user.name, role: user.role }
end
end
end
end
So, with this we have covered the basic concepts to keep in mind for building a third party api in an existing Rails application.