Androidはワンツーパンチ 三歩進んで二歩下がる

プログラミングやどうでもいい話

Rails + Grapeを使って超単純なapiを作ってみる

初心者の勉強記録です。
「〜らしい」や「っぽい」などの語尾が多くなっています。
手順を書き残すため、主題とは外れた内容も含みます。
間違ったり知識が最新でなかったりすると思いますのでツッコミ大歓迎でございます。


何をやりたいのか

Androidアプリのサンプル用に単純なAPIを作りたい→Grapeというgemを発見した
現在Railsに興味を持って勉強中なのでRailsを使いたい
サーバーにもデプロイしたい

Grapeの何がいいのか

Rest-LikeなAPIを簡単に作れるDSLらしい
きれいなコードを書けるらしい
使っている人が多そうで情報が豊富そう

Railsとgrapeを組み合わせる利点はあるのか

いい感じの形式を強制されるので、一度導入すれば後で拡張と保守が楽になりそう

サンプルで何を作るのか

固定のjson文字列を返すのみのAPIを作る

GET /api/v1/dummy_api/status
{ code: 1 }

公式サイトを拾い読みメモ

APIバージョンをパスに含める構造を取る
APIに関するコードベースをAPIモジュール配下に設置する
・format は:jsonが推奨されているらしく、xml
# We don't like xml anymore とか言われているが使用不可かどうかまでは調べてない

Controllerの定義は次のような形式になる

# app/controllers/api/v1/hussars.rb
module API
  module V1
    class Hussars < Grape::API
      version 'v1' # path-based versioning by default
      format :json # We don't like xml anymore

      resource :hussars do
        desc "Return list of hussars"
        get do
          Hussar.all # obviously you never want to call #all here
        end
      end
    end
  end
end

このコードだとクライアントから呼び出すパスは/v1/hussars.jsonで終わる

/v1/wings.json で終わるパスであればクラスの定義は↓になる。
API::V1::Wings → app/controllers/api/v1/wings.rb
/v2/hussars.json なら
API::V2::Hussars → app/controllers/api/v2/hussars.rb

APIバージョンについて、すべてのリソースをマウントする集約クラスが必要。

# app/controllers/api/v1/base.rb
module API
  module V1
    class Base < Grape::API
      mount API::V1::Hussars
      mount API::V1::Wings
    end
  end
end

v2だと app/controllers/api/v2/base.rb.

すべてのAPIバージョンを集約するクラスが1つ必要。

# app/controllers/api/base.rb
module API
  class Base < Grape::API
    mount API::V1::Base
    mount API::V2::Base
  end
end

最後に集約クラスをroutes.rbに記述する

# config/routes.rb
Monterail::Application.routes.draw do
  # ...
  mount API::Base => '/api'
  # ...
end

ルーティングはこのようになる

/api/v1/hussars.json -> API::V1::Hussars
/api/v1/wings.json -> API::V1::Wings
/api/v2/hussars.json -> API::V2::Hussars

ファイル構造はこのようになる


その他

簡単なアプリではクラスが多くなりすぎるように思えるが、すぐに元が取れる
rescue_from というのが例外のハンドリングメソッド
Swaggerと結合できる

実装を開始する

Railsアプリを新規で作成

$ rails new hogehoge

Railsにgrapeをインストール

Gemfileに記述する

gem 'grape'

$ bundle install ←記述後に実行しておく

config/application.rbを編集

config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]

app/api配下にapiを作るので、このフォルダを読み込むためにこの記述が必要

ルーティング

config/routes.rbを編集

mount API => '/'

APIクラスを設置する(リソースを集約したクラスでもある)

class API < Grape::API
  prefix 'api'
  version 'v1', using: :path
  format :json

  helpers do
    def dummy_code
      { code: 1 }
    end

    def err401
      error!('401 Unauthorized', 401)
    end
  end

  resource :dummy_api do

    get :status do
      dummy_code
    end

    get :secret do
      err401
    end
  end
end

rake routesと実行すると次のように表示される。Controllerのルーティングと違う。

$ rake routes
Prefix Verb URI Pattern Controller#Action
   api      /           API

このサイト様によると
・「desc〜」で機能の説明を記述できる
・「get」「post」「put」「delete」と、HTTPのメソッドに対応した処理を定義できる
・「params〜」でパラメータを定義し、「require」で必須かを定義している

プライベートメソッドはhelpers内で行うことになっているらしい

検証

ローカルサーバーを立ち上げる
$ rails s

curlコマンドで確認する
$ curl localhost:3000/api/v1/dummy_api/status
{"code":1}
目論見どおり文字列が返ってくる

サーバーにデプロイする

herokuのアカウントを持っているのでそこにデプロイ

$ heroku login
$ heroku create sakurabird1-grape-example
Creating ⬢ sakurabird1-grape-example... done
https://sakurabird1-grape-example.herokuapp.com/ | https://git.heroku.com/sakurabird1-grape-example.git

$ git push heroku master

このようなエラーメッセージが出るので

remote:        Make sure that `gem install sqlite3 -v '1.3.12'` succeeds before bundling.
remote:  !
remote:  !     Failed to install gems via Bundler.
remote:  !     
remote:  !     Detected sqlite3 gem which is not supported on Heroku.
remote:  !     https://devcenter.heroku.com/articles/sqlite3
remote:  !
remote:  !     Push rejected, failed to compile Ruby app.

deployment - Errors of pushing rails app to Heroku error occurred while installing sqlite3, and Bundler cannot continue - Stack Overflow
を参考にGemfileを直す

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug'
  gem 'sqlite3'
end

group :production do
  gem 'pg’
end

$ bundle install ←記述後に実行しておく

変更をcommitした後、最新のコードをHerokuにpush

$ git push heroku master

検証

$ curl https://sakurabird1-grape-example.herokuapp.com/api/v1/dummy_api/status
と実行すると
{"code":1}
とレスポンスが返ってくるので成功

このサンプル置き場

github.com