Michael Cordell's Blog

API with Padrino and Grape

02 Nov 2014

Recently, I have been exploring options for creating RESTful APIs in the Ruby. Concurrently, I have been investigating Sinatra and its father-framework padrino to create a lighter weight web app. While Sinatra in itself is a suitable solution for APIs, grape is a micro-framework specifically geared towards creating them. It also mounts well alongside Sinatra apps so it follows that it should play well with padrino. Below, I present a method for setting up a grape API in padrino and two methods for testing the API one using plain rspec with rack-test and another using airborne. The complete repo for this process can be found here at padrino-grape-example.

Mounting grape in padrino

Generate a new padrino app:

padrino g project padrino-grape-example --tiny

Note: If you are certain you would like to use plain rspec over airborne for testing you may add the following flag to simplify your setup:

padrino g project padrino-grape-example --tiny -t rspec

To the Gemfile we add the grape dependency and additionally we are going to use the gem grape-padrino which allows Grape APIs to be mounted easily in padrino. This gem adds in class methods to the stock grape API class that allows it to match up to the Padrino app class and therefore be mountable.

...
gem 'grape'
gem 'padrino-grape', github: "adamluzsi/padrino-grape"

After a bundle install, we can then add an api folder and our api.rb within it.

class Api < Grape::API
  include PadrinoGrape

  get :hello do
    { hello: "World" }
  end
end

Notice the inclusion of include PadrinoGrape, this is the addition of the class methods touched on earlier.

Finally, mount this file in Padrino by opening config/apps.rb and adding the following line:

Padrino.mount('Api', :app_file => Padrino.root('api/api.rb')).to('/api/')

This will mount the API to /api/, which can be tested by running the padrino app and visiting /api/hello.

Adding individual resources

Presumably, we will have an API with multiple resources, so to create a sane structure lets separate individual resources into their own files. In order to do this we must autoload the files within the API directory. Open config/boot.rb and add the following within the before_load block:

Padrino.before_load do
  Padrino.dependency_paths << Padrino.root('api/*.rb')
end

We can then add a new resource by adding a file to the api directory such as api/foo.rb:

class Api
  resource :foo do
    get :hello do
      { hello: "Foo says hello" }
    end
  end
end

Notice, that we are merely adding to the existing API class so that a new route exists on the api of /api/foo/hello.

Versioning the API

An important aspect of APIs that we must consider before heading to far down the rabbit hole is how we can version an API. Turns out this is fairly easy with grape, but lets make sure it works.

Open foo.rb and add the following line within the class:

version 'v1', using: :path

Saving the file and reloading the server allows us to see that /api/foo/hello is no longer accessible, rather it has moved to /api/v1/foo/hello. So far, so good, lets create a new version of the foo api at api/foo_two.rb:

class Api
  version 'v2', using: :path

  resource :foo do
    get :hello do
      { hello: "Foo two says hello" }
    end
  end
end

We can save and restart the server and see that visiting /api/v2/foo/hello gives us our desired message. This indicates that versioning works fine. Most likely we would want the two versions in separate folders but this is merely a proof of concept.

Testing with rspec and rack-test

To set up basic rspec testing with rack-test we add the following to the gemfile:

gem 'rspec', :group => 'test'
gem 'rack-test', :require => 'rack/test', :group => 'test'

Additionally you will want to add the boilerplate helpers that are generated by padrino for spec/spec_helper.rb and spec/spec.rake or you can copy them out of this commit. These merely allow the padrino app to be mounted by rack-test during testing.

This allows us to write a basic test for our hello end point at spec/api_spec.rb

require File.dirname(__FILE__) + "/spec_helper.rb"
require 'json'

describe "making a get request on hello" do
  it "responds with hello world json" do
    get "/api/hello.json"
    response = {hello: 'World'}.to_json
    expect(last_response.body).to eq response
  end
end

This runs and passes and is a perfectly acceptable way to test the API. However, lets turn to testing with airborne which I feel is slightly more elegant solution.

Testing with Airborne

Airborne is a gem for testing API that is built upon rspec. It provides a set of helper methods that look extremely promising for testing an API.

We can add it to the Gemfile and run bundle install:

gem 'airborne'

This allows us to write tests such as the following at spec/airborne_spec.rb:

require 'airborne'

#setup airborne
require File.expand_path(File.dirname(__FILE__) + "/../config/boot")
Airborne.configure do |config|
  config.rack_app = Padrino.application
end

describe "getting the api" do
  it "responds" do
    get "/api/hello.json"
    expect_json({hello: "World"})
  end
end

Obviously everything up to the describe block could be moved to spec_helper, it is all done in one file for demonstration purposes. We require the gem, load the padrino app, and configure airborne to use the padrino app. This allows us to make simple requests as we did in the rack-test. Much of the airborne documentation shows tests that hit a FQD, presumably running on a separate server from the tests themselves. This is useful to remember if we are testing non-rack based APIs, but in our case we would much prefer it all be handled by rack.

Conclusion

So there we have it, a simple grape API mounted within a padrino app and tested without our choice of airborne or simple rspec+rack-test . In brief, we mounted a single Grape API at the endpoint point /api/. We then extended created files for each resource and re-opened by the API class to add them. Finally, we showed two methods of testing our API via rspec.

comments powered by Disqus