Raisin
Elegant, modular and performant APIs in Rails
Installation
Add this line to your application's Gemfile:
gem 'raisin'
And then execute:
$ bundle
Or install it yourself as:
$ gem install raisin
Usage
Raisin is composed of two main elements : a router and a lists of API. An API is a file containing your endpoints, with their paths, implementation and documentation. The router is where you modelize your API, saying which API goes to which version.
Under the hood, raisin will generate classes (one for each enpoint), enabling us to use unit test for them, but keeping it transparent for Rails router.
Here's a basic example of an API
class UsersAPI < Raisin::API
get '/users' do
expose(:user) { User.all }
end
post do
expose(:user) { User.new(params[:user]) }
response do
user.save
respond_with(user)
end
end
end
Endpoints
Each endoint is defined by the http verb used to access it, its path and its implementation. When the path is omitted, raisin will default it to '/'. And since we are in the UsersAPI, raisin will set a prefix to 'users'. So the post do
is equivalent to post '/users' do
.
Same for the get method, since its path is the same as the prefix, it can be omitted.
The expose
method allows you to create variables elegantly and to access it in your endpoints body or your views (in you gems like jbuilder or rabl-rails). These exposed variables are evaluated in the response block so you have access to everything (like the params object).
The response block is where your endpoint logic goes. It can be optionnal, as you can see in the get method. If no response is specified, raisin will behave like a a standart rails controller method (thus trying to render a file with tht same name as the endpoint or fallback to API rendering)
We talk about the name of the endpoint but how is it determine? raisin is smart enough to recognize basic CRUD operations and RESTful endpoint
class UsersAPI < Raisin::API
get '/users' {} # index
post '/users' {} # create
put '/users/:id/foo' # foo
end
You can see theses names if you run rake routes
in your console. If you prefer to name your endpoint yourself, you can do it by passing an :as
option
get '/users', as: :my_method_name
Namespaces
You often have endpoint that have a portion of their path in commons, a namespace in raisin. The most used is RESTful applications is the "member" namespace, that look like /resource_name/:id
.
raisin provides both generic namespace and member out of the box
class UsersAPI < Raisin::API
namespace 'foo' do
get '/bar' {} # GET /foo/bar
end
member do
put do # PUT /users/:id
response do
user.update_attributes(params[:user])
respond_with(user)
end
end
end
end
You see that in the update
method we used a user variable. This is because you can also expose variable for a whole namespace, which does member automatically (the variable name will be the resource name singularize, 'user' in our example)
Namespaces can be nested.
Miscellanous
You can add single_resource
in your API for single resources.
Resources can be nested just as regular Rails. For example
class CommentsAPI < Raisin::API
nested_into_resource :posts
get '/comments/:id' do # GET /posts/:post_id/comments/:id
expose(:comment) { post.comments.find(params[:id]) }
end
end
Router
raisin router is similar to the routes.rb
in Rails. APIs that appears at the top have precedence on the ones after. Versionning is done by encapsulating APIs inside version
block. :all
can be used is a part of your api is accessible for all versions.
# /app/api/my_api.rb
class MyApi < Raisin::Router
version :v2, using: :header, vendor: 'mycompany' do
mount CommentsApi
end
version [:v2, :v1] do
mount PostsApi
mount UsersApi
end
version :all do
mount LoginApi
end
end
# /config/routes.rb
mount_api MyApi
Versionning can be done via the HTTP Accept header (application/vnd.mycompany-v1+json for example) or via the URL (/v1/users/). When using the header versionning, the vendor must be set. These options can be set globally when configuring raisin.
Configuration
Raisin.configure do |c|
c.version.using = :header
c.version.vendor = 'mycompany'
end
If you are using versionning via header, you also need to add a middleware to your application stack
#config/application.rb
config.middleware.use Raisin::Middleware
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request