Braque
Braque aims to provide a simple and familiar interface for setting up clients to interact with Hypermedia (hal+json) API services. It is a lightweight wrapper around Hyperclient and ActiveAttr.
Braque is an early-stage and exploratory project. That said, at Artsy, we've used Braque to quickly consume Gris Hypermedia APIs with great benefit.
Basic model setup
Braque::Model
is an ActiveSupport concern. You can use Braque::Model to map a remote resource to a class in your application. Do so by including Braque::Model in the class, defining the API service's root url (required), and listing attributes which we expect to receive from the API.
class Article
include Braque::Model
api_root_url Rails.application.config_for(:articles_service)['url']
attribute :id
attribute :title
attribute :body
attribute :summary
attribute :created_at
attribute :updated_at
end
Braque::Model adds familiar "Active Record"-like create
, find
, and list
class methods to the embedding class (Article
in the example above) as well as save
and destroy
instance methods. These methods wrap Hyperclient to make calls to a remote hypermedia API.
Usage
In a Rails app, once you've set up your model, you can use the following familiar syntax to query the remote API:
class ArticlesController < ApplicationController
before_filter :find_article, except: [:index, :new]
def index
@articles = Article.list(page: params[:page], size: params[:size])
end
def new
end
def create
@article = Article.create params[:article]
redirect_to article_path(@article)
end
def show
end
def edit
end
def update
@article = @article.save params[:article]
redirect_to article_path(@article.id)
end
def destroy
@article.destroy
redirect_to articles_path
end
private
def find_article
@article = Article.find(id: params[:id])
end
end
Relations
If your remote API includes associated resources in the _links
node for a given resource, you can use Braque's relations helpers to make navigating to those associated resources somewhat simpler.
For example if the remote API returns a response for the Book resource like this:
{
"id":1,
"title":"My Magazine",
"_links":{
"self":{
"href":"http://localhost:9292/magazines/1"
},
"articles":{
"href":"http://localhost:9292/articles?magazine_id=1{&page,size}",
"templated":true
}
}
}
And the remote API returns something like the following for a give Article resource:
{
"id":1,
"title":"My Article",
"magazine_id": 1,
"content":"Lorem ipsum...",
"_links":{
"self":{
"href":"http://localhost:9292/articles/1"
},
"magazine":{
"href":"http://localhost:9292/magazines/1"
}
}
}
In this situation you could choose to setup your Magazine and Article models to include has_many
and belongs_to
association helpers.
class Magazine
include ::Braque::Model
api_root_url 'http://localhost:9292'
has_many :articles
attribute :id
attribute :title
end
class Article
include ::Braque::Model
api_root_url 'http://localhost:9292'
belongs_to :magazine
attribute :id
attribute :content
attribute :magazine_id
end
This will allow you to retrieve associated resources without manually constructing a new link.
So instead of something like
magazine = Magazine.find id: article.magazine_id
you may use the belongs_to
helper to retrieve the associated resource more simply with
magazine = article.magazine
Similarly, the has_many
helper provides retrieval methods for accessing associated resources.
articles = magazine.articles
This method supports passing params to the remote API as well.
articles = magazine.articles(page: 2, size: 20)
Subclassing
Braque supports inheritance for shared setup across multiple models in your app that make calls to the same remote API.
In the following example, Article
and Author
classes will inherit the api_root_url
and id
, created_at
, and updated_at
attributes from RemoteModel
.
class RemoteModel
include Braque::Model
api_root_url Rails.application.config_for(:remote_service)['url']
attribute :id
attribute :created_at
attribute :updated_at
end
class Article < RemoteModel
attribute :title
attribute :body
attribute :summary
end
class Author < RemoteModel
attribute :first_name
attribute :last_name
end
Custom Headers
Braque also supports passing additional headers along with API requests to your remote provider API service.
- Defining an
accept_header
will replace Hyperclient's defaultAccept
header with the value you provide. - Defining an
authorization_header
with your model will result in this value being sent over in anAuthorization
header with your requests. - Defining an
http_authorization_header
with your model will result in this value being sent over in anHttp-Authorization
header with your requests.
To wit:
class Article
include Braque::Model
api_root_url Rails.application.config_for(:articles_service)['url']
Rails.application.config_for(:articles_service)['token']
accept_header Rails.application.config_for(:articles_service)['accept_header']
attribute :id
attribute :title
end