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.