12/08/2018, 16:17

RESTful API with Grape gem

In this post we'll make a Rails API using Grape gem. And we'll also attach Swagger UI with it to get a nice API interface to play with. So what is Grape? I can quote directly from their official docs, Grape is a REST-like API framework for Ruby. It's designed to run on Rack or complement ...

In this post we'll make a Rails API using Grape gem. And we'll also attach Swagger UI with it to get a nice API interface to play with.

So what is Grape? I can quote directly from their official docs,

Grape is a REST-like API 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.

We'll make two api for our user model. One POST api for creating a user and one GET api for get a user info.

Lets start by adding grape gem in our Gemfile.

gem "grape"

Make a folder named api inside our app/controller folder and create a new file base.rb inside this folder.

Grape APIs are Rack applications that are created by subclassing the API modules under Grape::API. So lets define our base class in base.rb file.

module API
  class Base < Grape::API
    mount API::V1::Base
  end
end

Here we define our API module and wrote a class Base which is inheriting Grape::API. We use mount to tell Rails that our API locates here. If you dont know about mount or engine already take a look here.http://guides.rubyonrails.org/engines.html

As Grape supports versioning, we can make a version friendly structure for our API. So we're going to make another folder v1 inside the api folder. For future version we can create v2, v3 and so on.

Inside v1 folder we'll create another base.rb file. This file will be used for mounting all the API classes that will nest inside v1 folder. Lets put these code here,

module API
  module V1
    class Base < Grape::API
      mount API::V1::Users
    end
  end
end

Here we already mount our upcoming api class Users. So make another file users.rb inside v1 folder. In this file we'll write our API for users.

module API
  module V1
    class Users < Grape::API
      include API::V1::Defaults

        desc "Create user"
        params do
          requires :name, type: String
        end
        post "/create" do
          User.create! name: permitted_params[:name]
        end

        desc "Get a user"
        params do
          requires :id, type: Integer
        end
        post "/login" do
          User.find_by! id: permitted_params[:id]
        end
      end
    end
  end
end

As you can see our first api takes a POST request with a param :name. We define this param type as string. If someone call this api without this param Grape will send back a error message. More info about params can be found in their nice documentation here. https://github.com/ruby-grape/grape#parameters When this api will be called with name param we will take the param and create a new user.

And the second api takes a GET request with a integer param :id. We'll use the id param to find the user and give a json response containing user info. We'll use a serializer for this after a while.

At the top of our class you can see I include a Defaults class. We'll use this class to define some helper methods. Create a new file defaults.rb inside v1 folder.

module API
  module V1
    module Defaults
      extend ActiveSupport::Concern

      included do
        prefix "api"
        version "v1", using: :path
        default_format :json
        format :json
        formatter :json, Grape::Formatter::ActiveModelSerializers

        helpers do
          def permitted_params
            @permitted_params ||= declared(params, include_missing: false)
          end
        end

        rescue_from ActiveRecord::RecordNotFound do |e|
          error_response(message: e.message, status: 404)
        end

        rescue_from ActiveRecord::RecordInvalid do |e|
          error_response(message: e.message, status: 422)
        end
      end
    end
  end
end

Here I declare a helper method permitted params which will be useful to identify those params which we mentioned in our api. We extend ActiveSupport::Concern class to handle the error events. We'll send error response in case if there is any error to create or find the user.

You can see at top we declare some basic settings which are mentioned in grape documents. And as a json formatter we choose ActiveModelSerializers. To use this we need to add this gem in our Gemfile.

gem 'grape-active_model_serializers'

Now write a serializer for our user model. Create user_serializer.rb inside app/serializers folder and add this codes.

class GraduateSerializer < ActiveModel::Serializer
  attributes :id, :name
end

We define these two attributes to show in our api response.

To access the api we need to initialize it in our router.

Rails.application.routes.draw do
  mount API::Base, at: "/"
end

And for cross origin resource sharing we'll use rack-cors gem. Add this in gemfile.

gem 'rack-cors', :require => 'rack/cors'

And add this code to your config/application.rb file.

module Api
  class Application < Rails::Application
    config.middleware.use Rack::Cors do
      allow do
        origins "*"
        resource "*", headers: :any, methods: [:get,
            :post, :put, :delete, :options]
      end
    end
    config.active_record.raise_in_transactional_callbacks = true
  end

Pretty much done. Finally we can add another gem for swagger documentation support.

gem "grape-swagger-rails"

Go to app/controllers/api/v1/base.rb and put some lines to enable swagger.

require "grape-swagger"

module API
  module V1
    class Base < Grape::API
      mount API::V1::Users

      add_swagger_documentation(
        api_version: "v1",
        hide_documentation_path: true,
        mount_path: "/api/v1/swagger_doc",
        hide_format: true
      )
    end
  end
end

We also need to define a route for this in the config/routes.rb file.

mount GrapeSwaggerRails::Engine, at: "/documentation"

Now start the rails server and go to http://localhost/documentation. In the url area filled it with our mounted path: http://localhost:3000/api/v1/swagger_doc And we're done. We got our two api with nice documentation ready to go.

0