[Rails4] Grape で API を簡単に実装 & API を複数バージョンで分ける方法


Grape を利用すると Restful な WEB-API が簡単に作成できるらしいので試してみました。その際、実際の運用を視野にいれて、次のこともやってみました。

  • 複数の API バージョン に対応する。
    • 例:http://ドメイン/api/v1/.. http://ドメイン/api/v2/..
  • 1つのソースがファットにならないよう モデル毎にソースを分ける

まずは Grape の導入手順から説明します。

Grape の導入手順

前提

  • 手元の OS:Mac OS X 10.9.5 (Mavericks)
  • Ruby:2.1.2
  • Rails:4.1.1

Grape は結果、0.9.0 が入りました。

手順

  • 適当な Rails プロジェクトを作成。

    $ bundle exec rails new grape_app --skip-bundle -T
    
  • Gemfile への追記。

    Gemfile
    #..
    
    gem 'grape'
    
    #..
    
  • gem をインストール。

    $ cd grape_app
    $ bundle install --path vendor/bundle
    

API の作成

前提

APIのソースコードは app/apis/ 下に置くこととします。ファイル構成はこんな感じ。

ディレクトリ構成およびファイル名は、クラスの名前空間およびクラス名と一致させる必要があります。 大文字/小文字は、気にしなくてもいいみたい。

手順

  • 適当に Scaffold します。

    $ ./bin/rails g scaffold person name:string age:integer memo:text
    $ ./bin/rails g scaffold product name:string price:integer memo:text
    $ ./bin/rake db:migrate
    

    Person モデルと Product モデルが作られます。

  • app/apis/ 下の Ruby ファイルが読み込まれるようにします。

    config/application.rb
    #..
    
    module GrapeApp
      class Application < Rails::Application
        #..  
    
        config.paths.add File.join('app', 'apis'), glob: File.join('**', '*.rb')
        config.autoload_paths += Dir[Rails.root.join('app', 'apis', '*')]
      end
    end
    
  • 基底となるAPIクラスを作成します。

    • このファイルで各バージョンのAPIをマウントします(今回は、バージョン1のみ)。
    app/apis/api/root.rb
    module API
      class Root < Grape::API
        # http://localhost:3000/api/
        prefix 'api'
    
        mount API::Ver1::Root
        #mount API::Ver2::Root
      end
    end
    
  • バージョン1の中で、基底となるAPIクラスを作成します。

    • ここで 各APIの実体をマウントします。
    app/apis/api/ver1/root.rb
    module API
      module Ver1
        class Root < Grape::API
          # http://localhost:3000/api/v1/
          version 'v1'
          format :json
    
          mount API::Ver1::People
          mount API::Ver1::Products
        end
      end
    end
    
  • 各APIの実体を作成します。

    • 今回は「全件取得」と「1件取得」のAPIを作成します。
    app/apis/api/ver1/people.rb
    module API
      module Ver1
        class People < Grape::API
          resource :people do
    
            # GET /api/v1/people
            desc 'Return all people.'
            get do
              Person.all
            end
    
            # GET /api/v1/people/{:id}
            desc 'Return a person.'
            params do
              requires :id, type: Integer, desc: 'Person id.'
            end
            get ':id' do
              Person.find(params[:id])
            end
          end
        end
      end
    end
    
    app/apis/api/ver1/products.rb
    module API
      module Ver1
        class Products < Grape::API
          resource :products do
    
            # GET /api/v1/products
            desc 'Return all products.'
            get do
              Product.all
            end
    
            # GET /api/v1/products/{:id}
            desc 'Return a product.'
            params do
              requires :id, type: Integer, desc: 'Product id.'
            end
            get ':id' do
              Product.find(params[:id])
            end
          end
        end
      end
    end
    
  • 最後に、基底となるAPIクラスをルーティングに追加します。

    config/routes.rb
    Rails.application.routes.draw do
    #..
    
      mount API::Root => '/'
    
    #..
    end
    

動作確認

適当にデータを登録し、ブラウザでアクセスしてみます。

おわりに

簡単に API が作成できました。自身の課題として、次のものがあります。

  • Grape の GitHubページ に記載されている機能を検証できていない
  • エラーのハンドリングまわりの実装
  • JSONデータの入れ子に対応できるよう、テンプレートエンジンを導入(rabl が良い?)

今回はわりとよい感触を得られたので、引き続き触って評価してみようと思います。