Day 5: Exploring Inspire’s Server-Side API w/Zach

It’s best to keep the API representation of an object separate from the object itself so even though Rails provides #as_json and #to_json methods. It can be tempting to use them, to evolve them, and to build around them, but in my experience it’s best avoid them.

APIs dont’t usually consist of all of the data that are housed in your system, so you need to selectively choose what to support on a public API. And then you need to be able to support versioning the API, permissions of what data can be seen, etc. I recently heard about Grape so I decided to give it a look.

Grape is a micro-framework for creating REST-like APIs, but it primarily appears to help with organizing API route/endpoint definitions. After a short spike it wasn’t solving my primary concern: cleaning how the API content is generated simple, clean, and as close to the end-point as possible. So I decided to explore rabl.

Rabl is a Ruby API templating language. This means that rather than using erb or haml to write your APIs, you write rabl templates. At first the syntax seemed a little strange, so I decided to spike on representing an endpoint which involved multiple objects and required nesting. Here’s two example rabl files from the spike.

First, the index template:

# index.json.rabl
object false
code(:people) do |m|
  @contacts.people.map do |c|
    partial("api/v1/contacts/_person", \:object => c)
  end
end

And the partial used for representing a person:

# _person.json.rabl
object @person
attributes :id, :first_name, :last_name, :title, :dob, :gender, :about

code(:websites) do
  if locals[:object].present?
    locals[:object].websites.map do |w|
      { 'type' => w.value_type, 'url' => w.value }
    end
  end
end

code(:phone_numbers) do
  if locals[:object].present?
    locals[:object].phone_numbers.map do |w|
      { 'type' => w.value_type, 'url' => w.value }
    end
  end
end

code(:email_addresses) do
  if locals[:object].present?
    locals[:object].email_addresses.map do |w|
      { 'type' => w.value_type, 'url' => w.value, 'default' => w.default }
    end
  end
end

code(:addresses) do
  if locals[:object].present?
    locals[:object].addresses.map do |w|
      {
        'type' => w.value_type,
        'address' => w.full_address(:include_country => true),
        'default' => w.default
      }
    end
  end
end

code(:instant_message_accounts) do
  if locals[:object].present?
    locals[:object].instant_message_accounts.map do |w|
      { 'type' => w.value_type, 'url' => w.value }
    end
  end
end

code(:groups) do
  if locals[:object].present?
    locals[:object].groups.map do |w|
      { 'name' => w.name, 'id' => w.id }
    end
  end
end

While there are some improvements that can be made with rabl’s syntax it promotes a good separation between the model and the API. I really liked that. This will help keep API versioning and endpoints cleanly separated from how the underlying models evolve or implementations change.

Advertisements

One Comment on “Day 5: Exploring Inspire’s Server-Side API w/Zach”

  1. nesquena says:

    Stumbled upon this blog post but glad to hear you found RABL helpful. If you have any suggestions or patches for improved syntax, I would be open to them it is still a fairly new project. Perhaps eventually rabl should support syntax layers, or perhaps thats too much.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s