A Ruby minimal, clean and complete stack for microservices
Intro
Microservices are now very common in various architectures and environments.
The Ruby word is traditionally bound to the microservice approach by the glorious minimal web framework Sinatra.
There are many good ruby micro frameworks but Grape coupled with Swagger UI via the grape-swagger gem really shines as a fast, clean and complete solution.
With Grape + Swagger UI we can build the Restful API service and have a good auto-generated GUI to test and debug the API.
A microservice example with Grape and Swagger
As example in this article we’ll build a simple microservice that exposes a Restful CRUD resource, notes.
The Note model will contain: author, title, body, summary and the timestamps. The API will expose the classic CRUD actions: create, read, update, delete and the list of the resources available.
All code is available in this public git repository.
Let’s create the basic app structure
Code reference: commit: Created basic app with a simple Grape API, /notes/test
Basic app structure:
app/ + core.rb config.ru Gemfile Procfile
We’ll use bundle for gems management and foreman for launch the application with the command foreman start.
The config.ru will requires rubygems, bundle, the core.rb and then run the application.
The core.rb mainly will require our application’s files; apis, models ecc…
Add Grape and our first endpoint
Grape is a REST-like API micro-framework for Ruby. It’s designed to run on Rack or complement existing web application frameworks such as Rails and Sinatra by providing a simple DSL to easily develop RESTful APIs.
For reference see this commit.
As a first step we must add the grape gem and require it in config.ru.
Then add the folder apis with the resource apis/notes.rb and a /test endpoint in it. Also ./app/apis/notes must be required in config.ru.
As you can see in the apis/notes.rb file there is 2 parts in the Grape DSL:
The configurations:
version ‘v1’, using: :header, vendor: ‘matteofuzz’
format :json
And the resource defnition:
resource ‘notes’ do
get ‘/test’ do
{ data: “TEST” }
end
end
Now let’s check that it works, browse to http://localhost:5000/notes/test and see that it responds with {“data”:”TEST”}.
Add Swagger and serve it with Rack
Code reference: commit: Added grape-swagger, swagger and configured rack for serving it.
Swagger is a simple yet powerful representation of RESTful API, enabling it for a service can give free interactive documentation, client SDK generation and discoverability.
In our example we want to add the swagger-ui, a clean html5 ui for our API.
First, add the gem grape-swagger that enables swagger for our resource adding the swagger documentation reference in app/apis/notes.rb:
add_swagger_documentation \
:info => {
:title => “Notes API”
},
:hide_documentation_path => true,
:mount_path => “/swagger_doc”,
:markdown => false,
:api_version => ‘v1’
Then download swagger-ui in the new directory public.
Now we have to serve the /public/swagger-ui. We can do it with Rack and the rack-fiber_pool gem adding this Rack configuration in config.ru:
use Rack::Static,
:urls => [“/images”, “/lib”, “/js”, “/css”],
:root => “public/swagger_ui”
map ‘/swagger-ui’ do
run lambda { |env|
[
200,
{
‘Content-Type’ => ‘txt-html’,
‘Cache-Control’ => ‘public, max-age=86400’
},
File.open(‘public/swagger_ui/index.html’, File::RDONLY)
]
}
end
Finally, test the API with swagger http://localhost:5000/swagger-ui.
Add ActiveRecord, then create the table and the model
ActiveRecord with its rake tasks
Code reference: commit: Added ActiveRecord with the grape-active-record gem
In order to add ActiveRecord to our application we can use the grape-active-record gem.
So, first add rake, mysql2 and grape-activerecord gems to our Gemfile.
Once we’ve bundled the gems we can configure the database connection. The simplest way is adding the good old config/database.yml file.
Then we must enable ActiveRecord connection management adding this line in config.ru:
use ActiveRecord::ConnectionAdapters::ConnectionManagement
Adding this Rakefile also gives us the db tasks,
require “bundler/setup”
require “grape/activerecord/rake”
namespace :db do
# Some db tasks require your app code to be loaded, or at least your gems
task :environment do
require_relative “app/core”
end
end
Run bundle exec rake -T and see all available tasks.
Table and model creation
Code reference: commit: Added migration, model and requireds.
Let’s create the database:
bundle exec rake db:create
Then the migration:
bundle exec rake db:create_migration NAME=create_table_notes
db/migrate/20151105214314_create_table_notes.rb
class CreateTableNotes < ActiveRecord::Migration
def change
create_table :notes do |t|
t.string :author
t.string :title
t.text :content
t.text :summary
t.boolean :private, default: false
t.integer :valuation
t.timestamps null: false
end
add_index :notes, :author
end
end
Now we can add our simple ActiveRecord model Note in /app/models/note.rb
class Note < ActiveRecord::Base
validates :author, :title, :content, presence: true
end
Add Restful CRUD resource endpoints
Code reference: commit Added RESTful resourse CRUD + list.
And finally it’s the time to code our CRUD actions, the desc and params methods give the swagger UI the description of the endpoint and the required and optional parameters with their types and descriptions.
The create endpoint with documentation and logic is:
desc ‘Create a note.’
params do
requires :author, type: String, desc: ‘Author’
requires :title, type: String, desc: ‘Title’
requires :content, type: String, desc: ‘Body’
optional :summary, type: String, desc: ‘Summary’
optional :private, type: Boolean, desc: ‘Private’
optional :valuation, type: Integer, desc: ‘Valuation’
end
post ‘/’ do
Note.create params
end
The Swagger UI now lists all endpoints,
Conclusions
Naturally this example is very simple, but it shows how clean and easy is to build a microservice and to document its API in Ruby with Grape and Swagger.
Another bonus of using Swagger is its wide adoption, many languages and frameworks are supported, so having a uniform UI and documentation for services developed in Java, Ruby, Python or other languages can’t be easier.