Building a Platform API on Rails

Introduction

In my last post, I discussed the failings of to_json in Rails and how we approached writing our public APIs using RABL and JSON Templates in the View. Since that post, we have continued expanding our Developer API and adding new applications to our Application Gallery.

The approach of using views to intelligently craft our JSON APIs has continued to prove useful again and again as we strive to create clean and simple APIs for developers to consume. We have used the as_json approach on other projects for years in the past and I can say that I have not missed that strategy even once since we moved to RABL. To be clear, if your APIs and data models are dead simple and only for internal or private use and a one-to-one mapping suits your needs, then RABL may be overkill. RABL really excels once you find yourself needing to craft flexible and more complex APIs rich with data and associated information.

The last post was all about explaining the reasons for abandoning to_json and the driving forces behind our creation of RABL. This post is all about how to actually use Rails 3 and RABL in a psuedo real-world way to create a public API for your application. In other words, how to empower an “application” to become a developer’s platform.

Authentication

The first thing to determine when creating a platform is the authentication mechanism that developers will use when interacting with your API. If your API is read-only and/or easily cached on a global basis, then perhaps no authentication is necessary. In most cases though, your API will require authentication of some kind. These days OAuth has become a standard and using anything else is probably a bad idea.

The good news is that we are well past the point where you must implement an OAuth Provider strategy from scratch in your application. Nevertheless, I would strongly recommend you become familiar with the OAuth Authentication Protocol before trying to create a platform. Starting with this in mind, your next task is to pick an OAuth gem for Ruby. Searching Github, you can see there are many libraries to choose from.

The best one to start with is Rails OAuth Plugin which will provide a drop-in solution both to consuming and providing APIs in your Rails 3 applications. The documentation is fairly decent and coupled with the introduction blog post, I think reiterating the steps to set this up would be unnecessary. Follow the steps and generate an OAuth Provider system into your Rails 3 application. This is what will enable the “OAuth Dance” where developers can generate a request token and sign with that token for a user to retrieve and access token. Once the developer gets an access token, they can use that to interact with your APIs. The easiest way for developers to consume your API is to use a “consumer” OAuth library such as Ruby OAuth or equivalent in the client language.

Building your API

Now that we have authentication taken care of in our application, the next step is to create APIs for developers to interact with on our platform. For this, we can re-use our existing controllers or create new ones. You already likely have html representations of your application for users to interact with. These views are declared in a “index.html.erb” file or similar and describe the display for a user in the browser. When building APIs, the simplest way to think about them is to think of the JSON API as “just another view representation” that lives alongside your HTML templates.

To demonstrate how easy building an API is, let’s consider a sample application. This application is a simple blogging engine that has users and posts. Users can create posts and then their friends can read their streams. For now, we just want this to be a blogging platform and we will leave the clients up to developers to build using our APIs. First, let’s use RABL by declaring the gem in our Gemfile:

# Gemfile
gem 'rabl'

Next, we need to install the new gem into our gemset:

$ bundle install

Next, let’s generate the User and Post tables that are going to be used in this example application:

$ rails g model User first_name:string last_name:string age:integer
$ rails g model Post title:string body:text user_id:integer
$ rake db:migrate db:test:prepare

Nothing too crazy, just defining some models for user and posts with the minimum attributes needed. Let’s also setup model associations:

# app/models/user.rb
class User < ActiveRecord::Base
  has_many :posts
end

# app/models/post.rb
class Post < ActiveRecord::Base
  belongs_to :user
end

Great! So we now have a user model and a post model. The user can create many posts and they can be retrieved easily using the excellent rails has_many association. Now, let’s expose the user information for a particular user in our API. First, let’s generate the controller for Users:

$ rails g controller Users show

and of course setup the standard routes for the controller in routes.rb:

# config/routes.rb
SampleApiDemo::Application.routes.draw do
  resources :users
end

Next, let’s protect our controller so that the data can only be accessed when a user has authenticated, and setup our simple “show” method for accessing a user’s data:

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_filter :oauth_required

  respond_to :json, :xml
  def show
    @user = User.find_by_id(params[:id])
  end
end

As you can see the controller is very lean here. We setup a before_filter from the oauth-plugin to require oauth authentication and then we define a “show” action which retrieves the user record given the associated user id. We also declare that the action can respond to both json and xml formats. At this point, you might wonder a few things.

First, “Why include XML up there if we are building a JSON API?” The short answer is because RABL gives this to us for free. The same RABL declarations actually power both our XML and JSON apis by default with no extra effort! The next question might be, “How does the action know what to render?”. This is where the power of the view template really shines. There is no need to declare any response handling logic in the action because Rails will automatically detect and render the associated view once the template is defined. But what does the template that powers our XML and JSON APIs look like? Let’s see below:

# app/views/users/show.rabl
object @user

# Declare the properties to include
attributes :first_name, :last_name

# Alias 'age' to 'years_old'
attributes :age => :years_old

# Include a custom node with full_name for user
node :full_name do |user|
  [user.first_name, user.last_name].join(" ")
end

# Include a custom node related to if the user can drink
node :can_drink do |user|
  user.age >= 21
end

The RABL template is all annotated above hopefully explaining fairly clearly each type of declaration. RABL is an all-ruby DSL for defining your APIs. Once the template is setup, testing out our new API endpoint is fairly simple. Probably the easiest way is to temporarily comment out the “oauth_required” line or disable in development. Then you can simply visit the endpoint in a browser or using curl:

// rails c
// User.create(...)
// rails s -p 3001
// http://localhost:3001/users/1.json
{
  user: {
    first_name: "Bob",
    last_name: "Hope",
    years_old: "92",
    full_name: "Bob Hope",
    can_drink: true
  }
}

With that we have a fully functional “show” action for a user. RABL also let’s us easily reuse templates as well. Let’s say we want to create an “index” action that lists all our users:

# app/views/users/index.rabl
object @users

# Reuse the show template definition
extends "users/show"

# Let's add an "id" resource for the index action
attributes :id

That would allow us to have an index action by re-using the show template and applying it to the entire collection:

// rails s -p 3001
// http://localhost:3001/users.json
[
  {
    user: {
      id : 1,
      first_name: "Bob",
      last_name: "Hope",
      years_old: "92",
      full_name: "Bob Hope",
      can_drink: true
    }
  },
  {
    user: {
      id: 2,
      first_name: "Alex",
      last_name: "Trebec",
      years_old: "102",
      full_name: "Alex Trebec",
      can_drink: true
    }
  }
]

Now that we have an “index” and “show” for users, let’s move onto the Post endpoints. First, let’s generate a PostsController:

$ rails g controller Posts index

and append the posts resource routes:

# config/routes.rb
SampleApiDemo::Application.routes.draw do
  resources :users, :only => [:show, :index]
  resources :posts, :only => [:index]
end

Now, we can fill in the index action by retrieving all posts into a posts collection:

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_filter :login_or_oauth_required     
  respond_to :html, :json, :xml      

  def index          
    @posts = Post.all      
  end  
end

and again we define the RABL template that serves as the definition for the XML and JSON output:

# app/views/posts/index.rabl  

# Declare the data source  
collection @posts  

# Declare attributes to display  
attributes :title, :body  

# Add custom node to declare if the post is recent 
node :is_recent do |post|      
  post.created_at > 1.week.ago
end

# Include user as child node, reusing the User 'show' template
child :user do
  extends "users/show"
end

Here we introduce a few new concepts. In RABL, there is support for “custom” nodes as well as for “child” association nodes which can also be defined in RABL or reuse existing templates as shown above. Let’s check out the result:

[
  {
    post: {
      title: "Being really old",
      body: "Let me tell you a story",
      is_recent: false,
      user: {
        id : 52,
        first_name: "Bob",
        last_name: "Hope",
        years_old: 92,
        full_name: "Bob Hope",
        can_drink: true
      }
    }
  },
  { ...more posts... }
]

Here you can see we have a full posts index with nested user association information and a custom node defined as well. This is where the power of JSON Templates lies. The incredible flexibility and simplicity that this approach affords you. Renaming an object or attribute is as simple as:

# app/views/posts/index.rabl

# Every post is now contained in an "article" root
collection @posts => :articles

# The attribute "first_name" is now "firstName" instead
attribute :first_name => :firstName

Another benefit is the ability to to use template inheritance which affords seamless reusability. In addition, the partial system allows you to embed a RABL template within another to reduce duplication.

Wrapping Up

For a full rundown of the functionality and DSL of RABL, I encourage you to check out the README and the Wiki. In addition, I would like to point out excellent resources by other users of RABL and encourage you to read teohm’s post as well as Nick Rowe’s overview of RABL as well. Please post any other resources in the comments!

My intention with this post was to provide a solid introductory guide for creating an API and a data platform easily in Rails. If there are topics that you would be interested in reading about more in-depth related to API design or creation or building out a platform, please let us know and we will consider it for a future post.

This entry was posted in All, Engineering and tagged , , , . Follow any comments here with the RSS feed for this post. Post a comment or leave a trackback: Trackback URL.

Add a Comment

Your email is never published nor shared.

*
*

38 Comments

  1. Nice – thanks for writing this up.

    What’s going on with the boolean attributes like User can_drink? In the user API responses, it’s returning as a string, either “true ” (with space) or “true” (without space). In the Post API with nested User objects it’s a proper boolean true.

    Cheers, Paul

    1. nesquena says:

      Yep purely a blog post typo. In RABL the boolean is already returned as a proper true boolean value. Fixed in the post now and thanks for raising the issue.

  2. Ken Paulsen says:

    Very well written post. And excellently timed as I was considering creating a simple public api on one of my upcoming applications.

    Could be worth pointing out that this line:
    attribute :first_name => :firstName
    should be placed in the app/views/users/show.rabl instead of the app/views/posts/index.rabl that is indicated at the top. At least that’s how I got it to work.

    1. nesquena says:

      Glad to hear you enjoyed the post. Let me know your experience after building your public API. Always interesting to hear other people’s experiences with API building.

  3. Fred Wu says:

    Any insights or advice for API versioning? :)

    1. nesquena says:

      Ah versioning, excellent question. I have an extension to RABL I use on one project that allows me to append a suffix to my view templates:

      index.v1.rabl
      index.v2.rabl

      and that corresponds with rendering a template based on the version specified in the API route. It is very hacky and custom right now, but perhaps this is common enough I should either include it in RABL or consider how to expose that functionality in a simpler way. The great thing about versions is they are usually just different representations (views) so splitting them up in templates works very cleanly. You could also just check the version in an “if” statement within a single template.

      1. nesquena says:

        Thanks for the link, very good article.

  4. Henning Kiel says:

    We are in the process of implementing an API for our web app, and basically came to the same conclusion (ie. just using as_json can get cumbersome very quickly).

    I have one question about your approach: You alias the user’s age attribute to years_old… but won’t this create a problem in the update action, if a consumer tries to update a record by just changing some values in the JSON hash and PUT’ing this back to the server?

    Have you encountered a similar situation while building your API and if so, any thoughts on this?

    Cheers,

    Henning

    1. nesquena says:

      Thanks for the question. If you rename your attributes in the RABL then how does the UPDATE work to correspond? This of course depends on your application. If the change is a formatting issue as I have seen in one of my projects (i.e :years_old => :yearsOld) then I have written a generic before filter to transform the attributes back to the underscore form. In the common cases, I am renaming read-only information that serves as a description of data (who wrote a comment, who created the bookmark). Typically in real world scenarios I reserve aliasing at the view level for situations in which this won’t end up conflicting with a user’s interaction later on. In that case in a more real world usage, I would “alias_attribute :age, :years_old” in the model level (since this is no longer a view concern when an UPDATE is going to understand it) and then just use “attribute :years_old” directly in the view. Of course, these decisions are sometimes specific to the API goals as well.

      1. Altonymous says:

        It’s been my experience that this doesn’t work in RABL. Maybe someone can help me understand why…

  5. Thanks for a great solution! I’m setting up an API now, and glad to have something like this to start with.

    I’d personally advise users to limit (if not completely abstain from) the use of nodes in views. In my mind, this is model territory. For instance, can_drink is easily test-driven at the model layer where it becomes freely available to the rest of the application. You can certainly TDD the view layer, but it’s more tedious and processor-intensive to do so.

    Again, thanks for the great tool. I’ll keep you posted on my progress, success or otherwise :)

    1. nesquena says:

      Yeah glad RABL has been helpful so far and I would love to hear your followup progress and any other feedback once you get further along.

      I would say nodes become useful when you need to customize a model-tested method with a parameter. One simple example:

      node :profile_image_url do |u|
         u.profile_image(@size).url
      end

      here’s another:

      node :user_liked_post do |p|
         p.liked_post?(@user)
      end

      And there are many other examples of where well-tested model level methods can be tweaked or transformed in a node for convenience. Don’t write them off wholesale :)

  6. You’re right, that seems like a great use case for them. And as you said, those methods can be model-tested, leaving the blocks short and sweet. That rocks.

    Do nodes have the potential to be abused? Of course. But you could say the same thing about Mountain Dew, and I wouldn’t want to live in a world without that :)

  7. [...] APIs with Ruby and Rabl This is a very good tutorial on how much you get for free when you use the Rabl gem with Ruby. [...]

  8. mistaguy says:

    Im creating an API using Oauth 2.0 and i can use signpost to access the API via android. my question is. How can i access the user_id from Oauth when i get an access token on my android so that i can display the current users details/profile?

  9. DanielZ says:

    very good tutorial! thank you! Do you have this api demo project somewhere on github?

    1. nesquena says:

      Take a look at: http://gemaweek.com/post/7313783147/episode5-rabl and https://github.com/GemAWeek/episode5 . Evan put up a nice screencast and has a repository containing the demo code! Hope that helps.

  10. bayja says:

    First, thank you for great gem and great post.

    I have a question. If I want to provide a user data with only recent 5 posts(created by the user). how do I limit the number of associated posts? It may not be the question directly related to RABL gem, but I’m trying to find a clean way to solve this matter and it’s little tricky..

    1. nesquena says:

      Here’s how you can do it. Setup a named scope for posts (Rails 3):

      class Post < AR::Base
        scope :recent, lambda { order('created_at DESC').limit(5) }
      end

      then create a method to wrap this in user:

      class User < AR::Base
        def recent_posts
          self.posts.recent
        end
      end

      Now in RABL:

      object @user
      child :recent_posts => :posts do 
         attributes :id, :title, :body
         # or extends "posts/show"
      end

      and that should get you most of the way there

  11. Kurt Sussman says:

    RABL allowed me to build an AJAX API in about 4 minutes (for the first controller)! Thanks so much.

    I’d like to return errors in JSON, though, and I’m not finding much on best practices for sending that data as JSON. I guess errors would be treated as a child, right?

    1. nesquena says:

      I can’t speak to canonical best practices for error messages. I have done it different ways in different apps. Something like:

      { error : { message : “Reason this is an error”, key : ‘invalid_foo’ } }

      worked well enough for me in the past. basically:

      object @false
      node :error do
      { :message => “Reason this is an error”, :key => “invalid_foo” }
      end

      Like I said not necessarily a best practice but I have done it like that before. Take it for what its worth. Glad you are enjoying RABL.

  12. paulawoodiii says:

    Do your collections always have the associated object inside of it? For example the post object is repeated in an array.
    I’d like to get something like {“object-type”:”user”,”list-items”[{},{},{},{}]}
    instead of {“list-items”[{"user":{}},{"user":{}},{"user":{}},{"user":{}}]}

    Thanks for the framework though, already saving me lots of time

    1. nesquena says:

      You could try setting the configuration

      Rabl.configure do |config|
      config.include_json_root = false
      end

      and see if you like that behavior better (it will globally remove those “user” roots.

  13. Rob says:

    This is amazing, thank you! I knew there had to be an easier way to do this.

  14. thewoolleyman says:

    Rabl is awesome! Saved me a ton of time. Thanks!

    My only request would be some more examples in the README. For example, nesting glue statements within other glue statements, child statements within glue statements, and vice versa. It took some experimenting to understand how this works, it would have been easier to see some examples first.

    1. nesquena says:

      Thanks for the suggestions. Do you have any examples to share? Would you consider putting them on the wiki. I tried to put a lot of documentation in the readme, but there’s so many different things people might try to do.

  15. vijay says:

    Rabl is Nice gem…. what bout grape? https://github.com/intridea/grape

  16. David Esposito says:

    I read this post, your original post about RABL and the RABL README but I am surprised that the Create/Update part of resource mapping isn’t addressed at all.

    For PUT/POST, is it still hand-to-hand combat to take the payload apart and assign it to the relevant ActiveRecord models?

  17. PascalTurbo says:

    Thanks for this great post.

    I’m building my API for an app affecting only a small group of users (something about 1000 – 2000).
    Now I’m struggling a bit bout OAuth.
    Maybe I didn’t understand it the right way: When using OAuth, users don’t have an Account on my App – they get Authorized via Facebook, Twitter, Google or someone else.
    But I’m sure that there will be users who don’t want to authorize against them – so do I need to implement a second Authorization for this users?

    PascalTurbo

  18. teohm says:

    Hi Nathan,

    FYI, my blog post (teohm’s post) has been moved to a new URL:

    http://teohm.github.com/blog/2011/05/31/using-rabl-in-rails-json-web-api/

    For those who interested in RABL, you should check out JBuilder [1] as well.

    [1] https://github.com/rails/jbuilder

  19. Tom Harrison says:

    Thanks very much for this post — very helpful. A friend who works at another company said they went with jBuilder and have regretted it.

    I respectfully decline to agree with you that implementing an OAuth2 provider is easy; I read the entire spec, and pretty much get it, but I couldn’t find much out there showing how to actually do it. Then again lots of things aren’t easy for me, so perhaps it’s just me ;)

    Anyway, great writeup. rabl is simple, powerful and mostly just rocks.

  20. Arash says:

    How to use oauth for after authentication? How to implement a standard ACL management under oauth standards?
    Thanks

  21. Ola says:

    Thank you! This was spot on what I needed!

  22. mxrguspxrt says:

    Using it and loving it!

  23. Hey, very nice post, it’s nice to understand quickly after reading this post about Rails API creation,

    Thanks again

    Bharat

  24. Helio says:

    Hey guys,
    I’m having an weird behavior when using caching in Rabl I’m wondering if anybody else here had a similar issue.

    I posted it on GitHub with more details and I appreciate any help.

    GitHub issue: https://github.com/nesquena/rabl/issues/449

    Thanks in advance,
    ./Helio

  25. http://www. says:

    If you are going for most excellent contents like me, only visit this web
    site all the time as it gives quality contents, thanks

One Trackback

  1. [...] APIs with Ruby and Rabl This is a very good tutorial on how much you get for free when you use the Rabl gem with Ruby. [...]