Grape
An opinionated framework for creating REST-like APIs in Ruby.
Install / Use
/learn @ruby-grape/GrapeREADME

What is Grape?
Grape is a REST-like API framework for Ruby. It's designed to run on Rack or complement existing web application frameworks such as Rails and Sinatra by providing a simple DSL to easily develop RESTful APIs. It has built-in support for common conventions, including multiple formats, subdomain/prefix restriction, content negotiation, versioning and much more.
Stable Release
You're reading the documentation for the next release of Grape, which should be 3.2.0. The current stable release is 3.1.1.
Project Resources
Grape for Enterprise
Available as part of the Tidelift Subscription.
The maintainers of Grape are working with Tidelift to deliver commercial support and maintenance. Save time, reduce risk, and improve code health, while paying the maintainers of Grape. Click here for more details.
Installation
Ruby 3.2 or newer is required.
Grape is available as a gem, to install it run:
bundle add grape
Basic Usage
Grape APIs are Rack applications that are created by subclassing Grape::API.
Below is a simple example showing some of the more common features of Grape in the context of recreating parts of the Twitter API.
module Twitter
class API < Grape::API
version 'v1', using: :header, vendor: 'twitter'
format :json
prefix :api
helpers do
def current_user
@current_user ||= User.authorize!(env)
end
def authenticate!
error!('401 Unauthorized', 401) unless current_user
end
end
resource :statuses do
desc 'Return a public timeline.'
get :public_timeline do
Status.limit(20)
end
desc 'Return a personal timeline.'
get :home_timeline do
authenticate!
current_user.statuses.limit(20)
end
desc 'Return a status.'
params do
requires :id, type: Integer, desc: 'Status ID.'
end
route_param :id do
get do
Status.find(params[:id])
end
end
desc 'Create a status.'
params do
requires :status, type: String, desc: 'Your status.'
end
post do
authenticate!
Status.create!({
user: current_user,
text: params[:status]
})
end
desc 'Update a status.'
params do
requires :id, type: String, desc: 'Status ID.'
requires :status, type: String, desc: 'Your status.'
end
put ':id' do
authenticate!
current_user.statuses.find(params[:id]).update({
user: current_user,
text: params[:status]
})
end
desc 'Delete a status.'
params do
requires :id, type: String, desc: 'Status ID.'
end
delete ':id' do
authenticate!
current_user.statuses.find(params[:id]).destroy
end
end
end
end
Rails 7.1
Grape's deprecator will be added to your application's deprecators automatically as :grape, so that your application's configuration can be applied to it.
Mounting
All
By default Grape will compile the routes on the first route, but it is possible to pre-load routes using the compile! method.
Twitter::API.compile!
This can be added to your config.ru (if using rackup), application.rb (if using rails), or any file that loads your server.
Rack
The above sample creates a Rack application that can be run from a rackup config.ru file with rackup:
run Twitter::API
(With pre-loading you can use)
Twitter::API.compile!
run Twitter::API
And would respond to the following routes:
GET /api/statuses/public_timeline
GET /api/statuses/home_timeline
GET /api/statuses/:id
POST /api/statuses
PUT /api/statuses/:id
DELETE /api/statuses/:id
Grape will also automatically respond to HEAD and OPTIONS for all GET, and just OPTIONS for all other routes.
Alongside Sinatra (or other frameworks)
If you wish to mount Grape alongside another Rack framework such as Sinatra, you can do so easily using Rack::Cascade:
# Example config.ru
require 'sinatra'
require 'grape'
class API < Grape::API
get :hello do
{ hello: 'world' }
end
end
class Web < Sinatra::Base
get '/' do
'Hello world.'
end
end
use Rack::Session::Cookie
run Rack::Cascade.new [Web, API]
Note that order of loading apps using Rack::Cascade matters. The grape application must be last if you want to raise custom 404 errors from grape (such as error!('Not Found',404)). If the grape application is not last and returns 404 or 405 response, cascade utilizes that as a signal to try the next app. This may lead to undesirable behavior showing the wrong 404 page from the wrong app.
Rails
Place API files into app/api. Rails expects a subdirectory that matches the name of the Ruby module and a file name that matches the name of the class. In our example, the file name location and directory for Twitter::API should be app/api/twitter/api.rb.
Modify config/routes:
mount Twitter::API => '/'
Zeitwerk
Rails's default autoloader is Zeitwerk. By default, it inflects api as Api instead of API. To make our example work, you need to uncomment the lines at the bottom of config/initializers/inflections.rb, and add API as an acronym:
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym 'API'
end
Modules
You can mount multiple API implementations inside another one. These don't have to be different versions, but may be components of the same API.
class Twitter::API < Grape::API
mount Twitter::APIv1
mount Twitter::APIv2
end
You can also mount on a path, which is similar to using prefix inside the mounted API itself.
class Twitter::API < Grape::API
mount Twitter::APIv1 => '/v1'
end
Declarations as before/after/rescue_from can be placed before or after mount. In any case they will be inherited.
class Twitter::API < Grape::API
before do
header 'X-Base-Header', 'will be defined for all APIs that are mounted below'
end
rescue_from :all do
error!({ "error" => "Internal Server Error" }, 500)
end
mount Twitter::Users
mount Twitter::Search
after do
clean_cache!
end
rescue_from ZeroDivisionError do
error!({ "error" => "Not found" }, 404)
end
end
Remounting
You can mount the same endpoints in two different locations.
class Voting::API < Grape::API
namespace 'votes' do
get do
# Your logic
end
post do
# Your logic
end
end
end
class Post::API < Grape::API
mount Voting::API
end
class Comment::API < Grape::API
mount Voting::API
end
Assuming that the post and comment endpoints are mounted in /posts and /comments, you should now be able to do get /posts/votes, post /posts/votes, get /comments/votes and post /comments/votes.
Mount Configuration
You can configure remountable endpoints to change how they behave according to where they are mounted.
class Voting::API < Grape::API
namespace 'votes' do
desc "Vote for your #{configuration[:votable]}"
get do
# Your logic
end
end
end
class Post::API < Grape::API
mount Voting::API, with: { votable: 'posts' }
end
class Comment::API < Grape::API
mount Voting::API, with: { votable: 'comments' }
end
Note that if you're passing a hash as the first parameter to mount, you will need to explicitly put () around parameters:
# good
mount({ ::Some::Api => '/some/api' }, with: { condition: true })
# bad
mount ::Some::Api => '/some/api', with: { condition: true }
You can access configuration on the class (to use as dynamic attributes), inside blocks (like namespace)
If you want logic happening given on an configuration, you can use the helper given.
class ConditionalEndpoint::API < Grape::API
given configuration[:some_setting] do
get 'mount_this_endpoint_conditionally' do
configuration[:configurable_response]
end
end
end
If you want a block of logic running every time an endpoint is mounted (within which you can access the configuration Hash)
class ConditionalEndpoint::API < Grape::API
mounted do
YourLogger.info "This API was mounted at: #{Time.now}"
get configuration[:endpoint_name] do
configuration[:configurable_response]
end
end
end
More complex results can be achieved by using mounted as an expression within which the configuration is already evaluated as a Hash.
class ExpressionEndpointAPI < Grape::API
get(mounted { configuration[:route_name] || 'default_name' }) do
# some logic
end
end
class BasicAPI < Grape::API
desc 'Statuses index' do
params: (configuration[:entity] || API::Entities::Status).documentation
end
params do
requires :all, using: (configuration[:entity] || API::Entities::Sta
