はじめに
grape, grape-entityを利用したRESTful API開発をする機会があるため、それらのGemの基本的な使い方について調べてみました。
grape, grape-entityとは?
https://github.com/ruby-grape/grape
https://github.com/ruby-grape/grape-entity
grapeはRESTful APIを構築できるRubyフレームワークで、Rails/Sinatoraなどを組み合わせたり、Rackアプリケーションとして単独で利用することができるとのことです。
grape-entityはgrapeと一緒に利用し、プレゼンテーション層を定義するためのGemとのことです。レスポンスの構造を管理し、API内で統一することができます。
grape
環境構築とAPIの基本
シンプルなRackアプリケーションとして簡単なAPIのサンプルを実装してみます。
まずは grape と rack をbundle installします。
source "https://rubygems.org"
gem 'grape'
gem 'rack'
以下の構成とします。
$ tree .
.
├── Gemfile
├── Gemfile.lock
├── app
│ └── api.rb # APIを定義
└── config.ru
Rackで起動できるようにします。
require_relative 'app/api'
run API
まずは Grape::API
を継承したAPIクラスを定義し、GET /hello
を実装します。
require 'grape'
class API < Grape::API
format :json
get '/hello' do
{
message: 'Hello, World!'
}
end
end
rackup
を実行し、rackを起動します。curlでRackサーバーの /hello
APIエンドポイントにリクエストしたところ、JSONレスポンスが返ってきました。
$ curl http://localhost:9292/hello
{"message":"Hello, World!"}
ルーティング定義
namespace
, resource
と複数の方法がある。
resource
resource を利用することで /books
, /users
など特定のリソースに対する操作を定義することができます。
require 'grape'
class API < Grape::API
format :json
resource :posts do
# GET /posts
get do
[
{
id: 1,
title: 'Post Title 1',
content: 'Post Context 1'
},
]
end
# GET /posts/:id
get ':id' do
{
id: params[:id],
title: 'Post Title 1',
content: 'Post Content 1'
}
end
end
end
namespace
namespaceは特定のリソースに依存しない名前空間の定義をすることができます。
require 'grape'
class API < Grape::API
format :json
# GET /admin/dashboard
namespace :admin do
namespace :dashboard do
get do
{
message: 'Admin Dashboard'
}
end
end
end
end
パラメーターとバリデーション
params
ブロックを利用することでパラメーターの定義をすることができます。
-
requires
: 必須パラメーターを定義 -
optional
: 任意パラメーターを定義
必須または任意によって requires
, optional
を使い分けます。
またパラメーターに対してバリデーションに設定ができます。ここではcategory
パラメーターの型をString、値には fiction
, non-fiction
のみに制限しています。
class API < Grape::API
format :json
resource :books do
params do
requires :category, type: String, values: ['fiction', 'non-fiction']
end
# GET /books/:id
get ':id' do
{
id: params[:id],
title: 'Book Title 1',
author: 'Author Name 1'
}
end
end
end
そのため、指定の値以外の値を渡すとエラーになります。
$ curl "http://localhost:9292/books/1?category=hoge"
{"error":"category does not have a valid value"}
grape-entity
Gemfile
に grape-entity
を追加して bundle install
します。
source "https://rubygems.org"
gem 'grape'
gem 'grape-entity'
gem 'rack'
Entity を定義することで、表示する属性を明示的に定義することができます。以下の構成で簡単なサンプルを作っていきます。
$ tree .
.
├── Gemfile
├── Gemfile.lock
├── app
│ ├── api.rb
│ └── entity
│ └── user_entity.rb
└── config.ru
まずは Grape::Entity クラスを継承した UserEntity クラスを用意します。 表示したい属性を expose で定義します。
require 'grape-entity'
class UserEntity < Grape::Entity
expose :id
expose :name
end
grape 側のAPIエンドポイントでEntityを利用していきます。例えばUSERS定数のような配列データがあったとします。
GET /users
で、present
メソッドに対象データを渡します。withオプションでどのエンティティクラスを使用するか定義します。これによりオブジェクトをエンティティを通じて整形できます。
require 'grape'
require 'grape-entity'
require_relative 'entity/user_entity'
class API < Grape::API
format :json
USERS = [
{
id: 1,
name: 'Foo Bar1',
email: 'foo1@example.com'
},
{
id: 2,
name: 'Foo Bar2',
email: 'foo2@example.com'
}
]
# GET /users
resource :users do
get do
present USERS, with: UserEntity
end
end
end
実際にリクエストをすると以下になりました。ユーザー配列にはメールアドレスも保持していましたが、エンティティでは表示する設定をしていなかったのでレスポンスとしては返ってきません。余計な情報を省くことができるので便利ですね。
$ curl "http://localhost:9292/users" | jq .
[
{
"id": 1,
"name": "Foo Bar1"
},
{
"id": 2,
"name": "Foo Bar2"
}
]
まとめ
grape, grape-entity を利用することで簡単にRESTful APIを実装できることが分かった。またバリデーション、エンティティなど便利な機能も揃っており、機能を活用することで迅速にAPI開発ができそうです。