SkillAgentSearch skills...

Grape

An opinionated framework for creating REST-like APIs in Ruby.

Install / Use

/learn @ruby-grape/Grape
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

grape logo

Gem Version test Coverage Status

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
View on GitHub
GitHub Stars10.0k
CategoryDevelopment
Updated1d ago
Forks1.2k

Languages

Ruby

Security Score

100/100

Audited on Mar 24, 2026

No findings