read

While developing a rich client side web application or mobile app, we need RESTful JSON API which interacts with the front-end javascript framework. Here you may use backbone.js, ember.js or angular.js on the front-end side of application.

Here we’ll be using Ruby on Rails on the back-end which will serve JSON API consumable by fron-end framework. If you look at the ruby toolbox you’ll see many API Builder gems available but it seems grape can be a good choice.

Grape Grape is a RESTful API microframework built to easily and quickly produce APIs for Ruby-rooted web applications.

Let’s see how we can build RESTful JSON apis using Grape library:

Getting Started

Add grape to your Gemfile and then run bundle install

gem 'grape'

Modularizing API directory structure

Place API files into lib/api. You need to create api folder inside lib directory.
As we are placing api directory inside lib you don’t need to explicitly load it inside application.rb
If you want to place api directory at some other place then add below lines to to application.rb

config.paths.add "app/api", glob: "**/*.rb"
config.autoload_paths += Dir["#{Rails.root}/app/api/*"]

First, Let’s create API::Root class that will mount available api versions.

# lib/api/root.rb
module API
class Root < Grape::API
prefix 'api'
mount API::V1::Root
# mount API::V2::Root (next version)
end
end

Now, create a API::V1::Root class that will mount resources for version 1

# lib/api/v1/root.rb
module API
module V1
class Root < Grape::API
mount API::V1::Posts
mount API::V1::Authors
end
end
end

Now, add resource Posts available for api access in json format

# lib/api/v1/posts.rb
module API
module V1
class Posts < Grape::API
version 'v1'
format :json
resource :posts do
desc "Return list of recent posts"
get do
Post.recent.all
end
end
end
end
end

Now, lets add one more resource Authors to version v1

# lib/api/v1/authors.rb
module API
module V1
class Authors < Grape::API
version 'v1'
format :json
resource :authors do
desc "Return list of authors"
get do
Author.all
end
end
end
end
end

Mounting API under rails routes

Mount API::Root under routes pointing to rails root

# config/routes.rb
SampleApp::Application.routes.draw do
mount API::Root => '/'
end

Customize JSON API Errors

We can control the api raised errors and customize them so that response is in our own format whenever there are exceptions.

# lib/api/error_formatter.rb
module API
module ErrorFormatter
def self.call message, backtrace, options, env
{ :response_type => 'error', :response => message }.to_json
end
end
end

Now, you can plug this module inside API::Root

# lib/api/root.rb
module API
class Root < Grape::API
#...
error_formatter :json, API::ErrorFormatter
#...
end
end

You can override error formatter for particular api version. Let’s customize errors for API::v1::Root:

#lib/api/v1/error_formatter.rb
module API
module V1
module ErrorFormatter
def self.call message, backtrace, options, env
{ :response_type => 'error', :response => message }.to_json
end
end
end
end
# lib/api/v1/root.rb
module API
module V1
class Root < Grape::API
#...
error_formatter :json, API::V1::ErrorFormatter
#...
end
end
end

Accessing API routes

If you do rake routes | grep api then it will list only mount path for api but do not list all the paths.

rake routes | grep api
api_root /api API::Root

So, in-order to list all api paths, you may have to create api routes task:

# lib/tasks/routes.rake
namespace :api do
desc "API Routes"
task :routes => :environment do
API::Root.routes.each do |api|
method = api.route_method.ljust(10)
path = api.route_path.gsub(":version", api.route_version)
puts " #{method} #{path}"
end
end
end

Now, run task and it should print routes like this:

rake api:routes
GET /api/v1/posts(.:format)
GET /api/v1/authors(.:format)

Securing API

Now we have got Grape API ready and working properly. Lets see how we can secure API. There are many approaches to authenticate API. Here lets first get it working with simple HTTP Basic authentication.

HTTP Basic authentication

In our case, lets add basic authentication to the API::Root and it will get applied to all versions of API.

# lib/api/root.rb
module API
class Root < Grape::API
#...
http_basic do |email, password|
user = User.find_by_email(email)
user && user.valid_password?(password)
end
#...
end
end

Requesting API using basic http auth credentials:

curl http://localhost:3000/api/products -u "admin:secret"

Authenticate using email and password

Grape provides us with before block inside that we can add authenctication code.

# lib/api/root.rb
module API
class Root < Grape::API
#...
before do
error!("401 Unauthorized", 401) unless authenticated
end
helpers do
def authenticated
user = User.find_by_email(params[:email])
user && user.valid_password?(params[:password])
end
end
#...
end
end
Blog Logo

Sandip Ransing


Published

Comments

Image

Fun On Rails

Journal of a Web Developer #ruby #rails #JS

Back to Overview