簡単な API サーバーを Ruby で作ろうと思って Rails じゃ大げさすぎるし Sinatra かなーと思ってたら Grape というのがあったので試してみてる。
自分用のメモなので、ちゃんと知りたい人は https://github.com/ruby-grape/grape/blob/master/README.md を読みましょう。
基本
Gemfile を作って、
source 'https://rubygems.org' gem 'grape'
bundle install
して、
api.rb という名前でファイルを作って(名前はなんでもいい)、
require 'grape' class API < Grape::API get :hoge do 'GET' end post :hoge do 'POST' end put :hoge do 'PUT' end delete :hoge do 'DELETE' end end
config.ru を作って、
require_relative 'api' run API
rackup --port 10080
コマンドで実行。1
こんな感じ。
~% curl -X GET -D- -d '' http://127.0.0.1:10080/hoge HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 3 Server: WEBrick/1.6.0 (Ruby/2.7.0/2019-12-25) Date: Mon, 24 Feb 2020 13:31:00 GMT Connection: Keep-Alive GET ~% curl -X POST -D- -d '' http://127.0.0.1:10080/hoge HTTP/1.1 201 Created Content-Type: text/plain Content-Length: 4 Server: WEBrick/1.6.0 (Ruby/2.7.0/2019-12-25) Date: Mon, 24 Feb 2020 13:31:06 GMT Connection: Keep-Alive POST ~% curl -X PUT -D- -d '' http://127.0.0.1:10080/hoge HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 3 Server: WEBrick/1.6.0 (Ruby/2.7.0/2019-12-25) Date: Mon, 24 Feb 2020 13:31:08 GMT Connection: Keep-Alive PUT ~% curl -X DELETE -D- -d '' http://127.0.0.1:10080/hoge HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 6 Server: WEBrick/1.6.0 (Ruby/2.7.0/2019-12-25) Date: Mon, 24 Feb 2020 13:31:12 GMT Connection: Keep-Alive DELETE
メソッドオーバーライド
_method=DELETE
つけて POST しても DELETE と見なしてくれないけど、
~% curl -X POST -d '_method=DELETE' http://127.0.0.1:10080/hoge POST
config.ru で Rack::MethodOverride
を使用するようにしておくと、
require_relative 'api' use Rack::MethodOverride run API
DELETE と見なしてくれる。
~% curl -X POST -d '_method=DELETE' http://127.0.0.1:10080/hoge DELETE
ネームスペース
namespace
を使ってこんな風にも書ける。
require 'grape' class API < Grape::API namespace :hoge do get do 'GET' end post do 'POST' end put do 'PUT' end delete do 'DELETE' end end end
リクエスト
リクエストのヘッダは headers
、パラメータは params
で Hash としてアクセスできる。
class API < Grape::API get :hoge do headers.inspect + "\n" + params.inspect + "\n" end end
~% curl 'http://127.0.0.1:10080/hoge?a=123&b=xyz' {"Host"=>"127.0.0.1:10080", "User-Agent"=>"curl/7.65.3", "Accept"=>"*/*", "Version"=>"HTTP/1.1"} {"a"=>"123", "b"=>"xyz"}
パラメータは params
で定義しておくとバリデーション&変換してくれる。
パス中に :識別子
で含めることもできる。
class API < Grape::API params do requires :id, type: Integer requires :s, type: String optional :p, type: Date end get 'hoge/:id' do params end end
~% curl 'http://127.0.0.1:10080/hoge/' 404 Not Found ~% curl 'http://127.0.0.1:10080/hoge/abc' id is invalid, s is missing ~% curl 'http://127.0.0.1:10080/hoge/123' s is missing ~% curl 'http://127.0.0.1:10080/hoge/123?s=hoge' {"s"=>"hoge", "id"=>123} ~% curl 'http://127.0.0.1:10080/hoge/123?s=hoge&p=abc' p is invalid ~% curl 'http://127.0.0.1:10080/hoge/123?s=hoge&p=2020-02-25' {"s"=>"hoge", "p"=>Tue, 25 Feb 2020, "id"=>123}
正規表現の指定も可。
class API < Grape::API params do optional :ipv4, type: String, regexp: /\A\d+\.\d+\.\d+\.\d+\z/ end get 'hoge' do params[:ipv4] end end
ただし、.
を含むパラメータはパス中には指定できない。
class API < Grape::API params do requires :ipv4, type: String, regexp: /\A\d+\.\d+\.\d+\.\d+\z/ end get 'hoge/:ipv4' do params[:ipv4] end end
~% curl 'http://127.0.0.1:10080/hoge/1.2.3.4' 404 Not Found
パス中のパラメータの正規表現を指定すればOK。この場合は \A
や \z
は不要らしい。
class API < Grape::API get 'hoge/:ipv4', requirements: {ipv4: /\d+\.\d+\.\d+\.\d+/ } do params[:ipv4] end end
~% curl 'http://127.0.0.1:10080/hoge/1.2.3.4' 1.2.3.4
レスポンス
ブロックを評価した結果のオブジェクトがレスポンスデータになる。
class API < Grape::API get :hoge do {abc: 123, xyz: 789} end end
~% curl -D- http://127.0.0.1:10080/hoge HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 25 Server: WEBrick/1.6.0 (Ruby/2.7.0/2019-12-25) Date: Mon, 24 Feb 2020 14:04:34 GMT Connection: Keep-Alive {:abc=>123, :xyz=>"hoge"}
リクエストの拡張子で Content-Type を指定することでそれっぽく自動変換してくれる。
~% curl -D- http://127.0.0.1:10080/hoge.txt HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 25 Server: WEBrick/1.6.0 (Ruby/2.7.0/2019-12-25) Date: Mon, 24 Feb 2020 14:04:36 GMT Connection: Keep-Alive {:abc=>123, :xyz=>"hoge"} ~% curl -D- http://127.0.0.1:10080/hoge.json HTTP/1.1 200 OK Content-Type: application/json Content-Length: 24 Server: WEBrick/1.6.0 (Ruby/2.7.0/2019-12-25) Date: Mon, 24 Feb 2020 14:04:38 GMT Connection: Keep-Alive {"abc":123,"xyz":"hoge"} ~% curl -D- http://127.0.0.1:10080/hoge.xml HTTP/1.1 200 OK Content-Type: application/xml Content-Length: 104 Server: WEBrick/1.6.0 (Ruby/2.7.0/2019-12-25) Date: Mon, 24 Feb 2020 14:04:40 GMT Connection: Keep-Alive <?xml version="1.0" encoding="UTF-8"?> <hash> <abc type="integer">123</abc> <xyz>hoge</xyz> </hash>
レスポンスステータスは status
で指定する。
class API < Grape::API get :hoge do status 400 'なんかおかしいよ' end end
~% curl -D- http://127.0.0.1:10080/hoge HTTP/1.1 400 Bad Request Content-Type: text/plain Content-Length: 24 Server: WEBrick/1.6.0 (Ruby/2.7.0/2019-12-25) Date: Mon, 24 Feb 2020 14:14:50 GMT Connection: Keep-Alive なんかおかしいよ
バージョニング
バージョニングができるのが特徴らしい。
require 'grape' class API1 < Grape::API version 'v1' get :hoge do 'Version 1' end end
require 'grape' class API2 < Grape::API version 'v2' get :hoge do 'Version 2' end end
require_relative 'api1' require_relative 'api2' use Rack::MethodOverride run Rack::Cascade.new [API1, API2]
こんな感じにしとくと、URL中のパスでバージョンを指定できるようになる。
~% curl http://127.0.0.1:10080/v1/hoge Version 1 ~% curl http://127.0.0.1:10080/v2/hoge Version 2
デフォルトはパスだけど、クエリパラメータで指定するようにすることもできるみたい。
ドキュメント
grape-swagger というのを使うと Swagger/OpenAPI 的にうまいことやってくれるらしい。そのうち調べる。
なんとなくよさそうなのでちょっと使ってみようかなーっと。
-
--port 10080
をつけているのは、デフォルトの 9292 だと Emacs の edit server と被るため。↩