Crafting Ruby Grape APIs: Partial Response (Part I)

In this article I am going to talk about a useful API development technique called Partial Response which can be used in Ruby Grape API’s to allow clients retrieving just the amount information they need instead of the full resource representation. This techinque reduces bandwidth usage and increases client efficiency and responsiveness, specially in mobile devices.

This is the first part of a series of posts I am going to write about crafting Ruby API’s with the Grape framework.

The most common approach to Partial Response is to include a fields=field1,field2,field-n parameter in the request specifing the necessary fields.

For example:

http://api.domain.com/v1/projects?fields=name,description

The above url will only retrieve the name and description fields instead of the entire resource representation which may include unnecessary fields and embedded documents.

More advanced techinques like Google’s allows clients to retrieve not only the necessary fields of the resource but also the fields of embedded resources.

The first step to implement Partial Response in Grape is to add the grape-entity gem to your Gemfile.

Grape Entities are a reusable means for converting Ruby objects to API responses. Entities can be used to conditionally include fields, nest other entities, and build ever larger responses, using inheritance.

gem "grape-entity"

Grape ships with a DSL to easily define entities within the context of an existing class.

class Project
  include Mongoid::Document
  include Grape::Entity::DSL

  field :name, type: String
  field :description

  entity do
    expose :_id, :if => lambda { |project, options| options[:fields].include?(:_id) }
    expose :name, :if => lambda { |project, options| options[:fields].include?(:name) }
    expose :description, :if => lambda { |project, options| options[:fields].include?(:description) }
  end
end

The above will automatically create a Project::Entity class and include the _id, name and description fields in the API response if they are included in the client specified options[:fields] parameter.

Finally, instruct grape to render the response using the Project::Entity class with the client requested fields.

class API < Grape::API

  resources :projects do
    desc "Get projects"
    params do
      optional :fields, type: String, desc: "Project fields."
    end
    get do
      present Project.all, with: Project::Entity, fields: parse_fields(Project, params[:fields].try(:split, ","))
    end
  end
end

The #parse_fields method is a simple method that returns an array of symbols with the requested fields or all the model fields if the &fields=field1,field2,field-n parameter was not specified.

helpers do

  def parse_fields(klazz, fields)
    fields ? fields.map { |field| field.to_sym } : klazz.fields.map { |field| field.first.to_sym }
  end
end

Resources

I hope you liked the implementation. In a future update or blog post I’ll include the code to restrict embedded documents fields.

Thanks for reading and see you in the next post of the “Crafting Ruby Grape API’s” series.

ruby

Comments

comments powered by Disqus