Why You Should Taste Grape

Hi folks, let's talk about APIs today.

I know that Rails 5 with --api mode is around the corner. But I need to say you how awesome is to build APIs with Grape. I will show you why.

What are the main features we expect from a good API?

  • Parameter validation
  • Parameter coercion
  • Documentation
  • Serialization
  • Performance
  • Versioning
  • Authentication
  • Testing

The first four items are the ones in which Grape really shines for me. I am going through one at a time.

Getting Started

For this tutorial, let's suppose we need to build an API to create and list customers.

require 'grape'

module MyAwesomeApi
  class Customer < Grape::API
    format :json

    resource :customers do
      post "/" do
        Customer.create(params)
      end

      get ":id" do
        { customer: Customer.find(params[:id]) }
      end
    end
  end
end

Ok, this is our starting point. Anyone used to Rails can grasp this snippet.

We have two routes, the first to create and the second to get customers.

Parameter validation

Something nice in Grape is that we can declare and validate the expected params in every route.

Parameter validation is as simple as telling Grape what we want. Given our base code above, we can simply add some validation by doing:

resource :customers do
  params do
    requires :first_name, type: String, regexp: /[A-z]+/
    optional :last_name, type: String
    requires :gender, values: %w(m f), default: "m"
    requires :birthdate, type: Date, allow_blank: false
  end
  post "/" do
    Customer.create(declared(params, include_missing: false))
  end
end

declared(params, include_missing: false) returns only declared params which aren't missing (has some value).

If any validation fails, a 400 HTTP status code will be automatically returned with the error(s).

Parameter coercion

Sometimes we need to treat some param before using it. Look how easy it is:

resource :customers do
  params do
    # ...
    requires :occupation, coerce_with: ->(obj) { obj.strip }
  end
  post "/" do
    Customer.create(declared(params, include_missing: false))
  end
end

Here we strip the occupation param.

Documentation

A good API must be well documented.

resource :customers do
  desc "Creates a new customer",
       http_codes: [
         { code: 201, message: "Created successfully" },
         { code: 400, message: "Invalid params" },
         { code: 401, message: "Not authorized" }
       ]
  params do
    # ...
    requires :first_name, desc: "Customer's first name"
  end
  post "/" do
    Customer.create(declared(params, include_missing: false))
  end
end

We are able to document the response of each endpoint. Check grape-swagger to see what you can do with it.

Serialization

With grape-entity we can organize our serialization code and keep it dry:

# entities/customer.rb
module Entities
  class Customer < Grape::Entity
    expose :id
    expose :first_name
    expose :last_name
    expose :avatar_url do |instance, _options|
      instance.avatar.url
    end
  end
end
get ":id" do
  customer = Customer.find(params[:id])
  present customer, with: Entities::Customer
end

It will return only the specified attributes.

Ok, it seems nice, but..

I like Rails. Do I have to stop using it to use Grape?

No! You can mount a Grape API inside your Rails application (or Sinatra, Rack..).

Read Grape's readme and give it a try.

Enjoy yourself.

Written on April 14, 2016

Share: