Grape APIs - Few Quick Tidbits

Jan 21 2012

It’s been pretty quiet here for a long time, but I’ve been busy building a few businesses, one of which includes a new product I’m helping to build and launch with a couple friends. One of the things I’ve been building is a robust REST API using Grape. If you want a quick overview, check out their README on the GitHub repo. I’ve really enjoyed using Grape and figured I’d share a few little tidbits of how I’m using it so far.

Configuring URL prefix

For a long time I was just using Rails 3 to “mount” my Grape API class like this:

routes.rb
mount MyApp::API => '/api'

This worked well for a while, but then I had a need to plug in some Rack middleware (such as Rack::Cors, which I plan to blog about soon). Long story short, I couldn’t mount it the same way anymore, but still needed to configure the base URL for the API to point to /api. Grape makes this really easy.

my_app_api.rb
module MyApp
  class API < Grape::API
    prefix 'api'
  end
end

Quick side note, I do still mount the API in the Rails routes for the test environment only for my automated API integration tests. But since I already have the prefix defined in the API, I just mount it to root for these purposes.

routes.rb
if Rails.env == 'test'
  mount MyApp::API => '/'
end

Delegate to the Rails logger (if desired)

my_app_api.rb
logger Rails.logger

Simple.

Request logging, including body

my_app_api.rb
before do
  # Of course this makes the request.body unavailable afterwards.
  # You can just use a helper method to store it away for later if needed. 
  Rails.logger.info "Request Body: #{request.body.read}"
end

Easy peasy.

Global exception handling

my_app_api.rb
rescue_from :all do |e|
  # Log it
  Rails.logger.error "#{e.message}\n\n#{e.backtrace.join("\n")}"

  # Notify external service of the error
  Airbrake.notify(e)

  # Send error and backtrace down to the client in the response body (only for internal/testing purposes of course)
  Rack::Response.new({ message: e.message, backtrace: e.backtrace }, 500, { 'Content-type' => 'application/json' }).finish
end

Handy.

HTTP Basic Authentication

my_app_api.rb
http_basic do |username, password|
  User.authenticate!(username, password)
end

No sweat.

Helper methods

my_app_api.rb
helpers do
  def current_context
    @context = # some call to retrieve context based on API key maybe?
  end

  # anything else needed by a group of or all resource/verb blocks
end

Straightforward.

Sending back errors with validation messages

my_app_api.rb
resource :turtles
  post do
    # make sure you filter/whilelist the request body attributes!
    turtle = Turtle.create(JSON.parse(request.body.read))
    error!(turtle.errors.messages, 400) unless turtle.valid?
  end
end

Smooth sailing.

General API building tips

I can’t recommend enough the article that John Nunemaker wrote recently on Creating an API. A great read with some great tips.

That’s all for now. Happy coding! :)