Grape API Mounted on RACK w/ Static Pages

Back | rack, grape, ruby | 1/30/2012 |

tl;dr – the source is here

Here’s how I mount a Grape API on Rack and also serve static pages. Most useful for building a service with documentation.

Setup bundler with the required gems. I am using the next version of Grape (frontier branch and the next version of rack-contrib which contains Rack::TryStatic that we’re going to want to use).

  1. source "http://rubygems.org"
  2.  
  3. gem "rack", "1.3.5"
  4. gem "rack-contrib", :git => "https://github.com/rack/rack-contrib.git", :require => "rack/contrib"
  5. gem "grape", :git => "http://github.com/intridea/grape.git", :branch => "frontier"

Static content goes to a new public folder, for example public/index.html.

Our application will need to boot with all these gems, we’ll do it Rails-style by starting with a config/boot.rb file. It brings in Bundler.

  1. require 'rubygems'
  2. require 'bundler/setup'

Lets define our Acme Grape API that will have a system resource that answers ping requests with “pong”. This goes into api/api.rb.

  1. module Acme
  2.   class API < Grape::API
  3.     version 'v1', :using => :header, :vendor => 'acme', :format => :json    
  4.     resource :system do
  5.       desc "Returns pong."
  6.       get :ping do
  7.         "pong"
  8.       end
  9.     end
  10.   end
  11. end

The application requires Bundler, all the gems in Gemfile and our API. Lets define it in config/application.rb.

  1. require File.expand_path('../boot', __FILE__)
  2.  
  3. Bundler.require :default, ENV['RACK_ENV']
  4.  
  5. require File.expand_path('../../api/api', __FILE__)

Note the odd File.expand_path construct, borrowed from Rails, - it translates a relative path to the current file into an absolute path, there’re allowing us to run the application from any directory – useful for hosting where you never know who boots the application.

Continuing to borrow from Rails, we will want different environments (development, production, etc.), so it’s a good idea to keep things organized. Setup the environment in config/environment.rb and then load the application.

  1. ENV['RACK_ENV'] ||= :test
  2.  
  3. require File.expand_path('../application', __FILE__)

Finally, we need to “rackup” this whole thing in config.ru. We will use Rack::TryStatic to serve static pages when available and pass through to the Acme API otherwise.

  1. require File.expand_path('../config/environment', __FILE__)
  2.  
  3. use Rack::TryStatic,
  4.   :root => File.expand_path('../public', __FILE__),
  5.   :urls => %w[/], :try => ['.html', 'index.html', '/index.html']
  6.  
  7. run Acme::API

Run the application with bundle exec rackup.

image

Full source on Github.