Crafting Ruby Grape APIs: Partial Response (Part I)
By Nicolás Garnil on
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.
See you on the next post of the “Crafting Ruby Grape API's” series.
