API with Padrino and Grape
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.