Ruby
Article

Build Great APIS with Grape

By Jay Raj

big_grape

APIs are needed for various reasons, such as by mobile apps to interact with backend CRUD operations. Creating maintainable APIs is not a simple task. As the application grows, so does the need to version and maintain the API.

In this tutorial, we’ll have a look at Grape, a micro-framework for creating maintainable REST-like APIs in Ruby.

From grape’s Github page:

Grape is a REST-like API micro-framework for Ruby. It’s designed to run on Rack or complement existing web application frameworks such as Rails and Sinatra by providing a simple DSL to easily develop RESTful APIs. It has built-in support for common conventions, including multiple formats, subdomain/prefix restriction, content negotiation, versioning and much more.

Our Project

We’ll be using Rails and grape together to create the API. It will expose the Employee model to other applications, consuming the back-end CRUD operations for that model.

The API can be used by rich web applications, like the ones created using AngularJS, Backbone.js, etc. or native mobile applications to create, edit, update, and delete employees.

Here are the API endpoints we’ll be creating:

//Get all employee details
GET /emp_api       

//Add an employee
POST /emp_api/  {name:"Jay", address:"Kerala, India", age:"34"}             

//Delete an employee         
DELETE /emp_api/1

//Update an employee
PUT /emp_api/1

Getting Started

Let’s start by creating a Rails application and installing the Grape gem. I’m using Rails 4.1.6.

Create a new Rails project, skipping the bundle install.

rails new emp_api --skip-bundle

Once the project files are all created, navigate to the project directory (emp_api). Open the Gemfile and include the grape gem at the end of the file.

gem 'grape'

Next, install all the required gems using Bundler:

bundle install

Generate an Employee model for the basic CRUD operations to be exposed by the API:

rails g model EmpData name:string address:string age:integer
rake db:migrate

All the API files will live inside the app folder. Create a new folder called api inside emp_api/app/.

Inside emp_api/app/api create another folder called employee, and a file in that directory called data.rb. This file holds the class to access the Employee model.

Inside emp_api/app/api/ create another file called api.rb where we’ll mount the class defined in emp_api/app/api/employee/data.rb.

As per the instructions on the Grape GitHub pages, since we have placed our API code in app/app, that directory needs to be added to the load/autoload paths. Set it up in config/application.rb as shown below:

require File.expand_path('../boot', __FILE__)

require 'rails/all'

Bundler.require(*Rails.groups)

module EmpApi
  class Application < Rails::Application
    ## Newly Added code to set up the api code
    config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
    config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
  end
end

Creating the API

Grape APIs are Rack applications that are created by subclassing Grape::API. Let’s get started by creating the first API endpoint to retrieve all employee details. Open up app/api/employee/data.rb and create the module class as shown:

module Employee
  class Data < Grape::API

    resource :employee_data do
      desc "List all Employee"

      get do
        EmpData.all
      end

    end

  end
end

We need to access the Employee::Data class functionality inside the API class. So, we’ll use mount to make Employee::Data class accessible inside the API class.

So, open up app/api/api.rb and mount Employee::Data

class API < Grape::API
  prefix 'api'
  version 'v1', using: :path
  mount Employee::Data
end

Open up app/config/routes.rb and include the following code to route the API calls to our root address:

Rails.application.routes.draw do
  mount API => '/'
end

Fire up the rails server:

rails server

Open a new terminal and try :

curl http://localhost:3000/api/v1/employee_data.json

You’ll see [] as response, since we don’t have any data yet. We don’t have any way to add employees, so let’s fix that.

Add another API endpoint to create a new employee. We’ll need certain values to create an employee, such as name, address, and age. Grape allows us to define whether these parameters are required or optional in the API itself.

Open app/api/employee/data.rb and add the following code to create the API endpoint for employee creation.

desc "create a new employee"
## This takes care of parameter validation
params do
  requires :name, type: String
  requires :address, type:String
  requires :age, type:Integer
end
## This takes care of creating employee
post do
  EmpData.create!({
    name:params[:name],
    address:params[:address],
    age:params[:age]
  })
end

Restart the server and use curl to post a request to the endpoint:

curl http://localhost:3000/api/v1/employee_data.json -d "name=jay;address=delhi;age=25"

Now, we should have an employee:

curl http://localhost:3000/api/v1/employee_data.json

[{"id":1,"name":"jay","address":"delhi","age":25,"created_at":"2014-10-27T16:43:49.303Z","updated_at":"2014-10-27T16:43:49.303Z"}]

Bingo.

The delete and update API for the employee model follow the similar pattern to our read and create endpoints.
The delete endpoint requires the employee ID:

# app/api/employee/data.rb

 desc "delete an employee"
 params do
   requires :id, type: String
 end
 delete ':id' do
   EmpData.find(params[:id]).destroy!
 end

Restart the Rails server and test the API by deleting the employee we created earlier.

curl -X DELETE http://localhost:3000/api/v1/employee_data/1.json

{"id":1,"name":"jay","address":"delhi","age":25,"created_at":"2014-10-27T16:43:49.303Z","updated_at":"2014-10-27T16:43:49.303Z"}

Now, if you check the employee listing api, you should get a empty response.

Next, in order to update an employee details, we’ll need to pass the employee ID and the values to be updated. Add the following code to create an API endpoint to update the employee address:

# app/api/employee/data.rb

desc "update an employee address"
params do
  requires :id, type: String
  requires :address, type:String
end
put ':id' do
  EmpData.find(params[:id]).update({
    address:params[:address]
  })
end

But we don’t have an employee to update. Remember, we deleted the last employee, so we’ll need to add one back in order to update it.

Restart the server and add a new employee first,

curl http://localhost:3000/api/v1/employee_data.json -d "name=roy;address=kerala;age=25"

{"id":2,"name":"roy","address":"kerala","age":25,"created_at":"2014-10-27T16:59:54.090Z","updated_at":"2014-10-27T16:59:54.090Z"}

Now, use the following command to update the address of the new employee:

curl -X PUT http://localhost:3000/api/v1/employee_data/2.json -d "address=mumbai"

true

Wrapping It Up

In this tutorial, we got started creating a simple CRUD API using the Grape framework. Detailed documentation regarding Grape is available on their GitHub pages.

Code from this tutorial is available on GitHub.

  • Chase Pursley

    Very helpful tutorial! I just discovered Napa, a Grape-based framework for building RESTful database APIs with Ruby. It brings it together with things we all love from Rails like ActiveRecord, Rake and generators that make for a productive framework: https://github.com/bellycard/napa

  • Opcomms Hq

    Hey, thanks for this tutorial. Can anyone see why this code won’t work? I’ve added another endpoint to retrieve employee by id, but get a No route matches error.

    params do

    requires :id, type: String

    end

    get ‘:id’ do

    EmpData.find(params[:id])

    end

    Error:
    No route matches [GET] “/api/v1/employee_data.json”

  • ohdowload

    I got {“error”:”address is missing, age is missing”} error when I want to create a new employee. What cause this problem? Please somebody help me out. Yeah I copied the code from this site but still got the same error. :)

  • ohdowload

    I got {“error”:”address is missing, age is missing”} error when I want to create a new employee. What cause this problem? Please somebody help me out. Yeah I copied the code from this site but still got the same error. :)

  • http://blog.mh-praxis.com Mirzalazuardi

    I want to correct this article. please replace all model ‘EmpData’ with ‘EmpDatum’. just remind model name it should be in singular

    • http://www.tinkererforlife.com/ anmol

      Thanks for that…I was thinking where did I go wrong :)

      • http://blog.mh-praxis.com Mirzalazuardi

        i’m glad to hear that correction helped you out :)

  • Carlos Cordoba

    Hi, trying with this, curl http://localhost:3000/api/v1/employee_data.json -d “name=jay;address=delhi;age=25”, the line does not work, I have already installed curl but the terminal just do nothing and the terminal results does not show any action on rails app. Could you please help me out with this? Thanks

  • Farihul Rouf

    How to resolve problem undefined method `call’ for Data:Class

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in Ruby, once a week, for free.