LoginSignup
134
133

More than 5 years have passed since last update.

RailsでJSON APIを定義するならRABLが便利!

Posted at

何が問題かというと

RailsでJSON APIを定義する時、素のままでやろうとすると
コントーラでto_jsonを呼んだり、モデルにas_jsonを定義したりすることになるかと思います。

モデルに書くとAPIによって出力内容を変えたい場合にとても苦労します。
API数が増えれば増えるほどモデルが複雑になっていきます。

APIレスポンスとしてのJSONはコントローラやモデルに書くべきでしょうか?
ビューに書いた方が自然ではないでしょうか?

RABL

とはいえ、他の画面用のビューのようにERBでJSONレスポンスを書くというのはないでしょう。
そこで、JSONのAPIレスポンスを表現することに特化したDSLライブラリのRABLが使えます。

http://nesquena.github.com/rabl/
https://github.com/nesquena/rabl
http://engineering.gomiso.com/2011/06/27/building-a-platform-api-on-rails/

インストール

RABLをインストールするにはGemfileに以下の行を追加して、bundle installします。

Gemfile
gem 'rabl'

APIコントローラ

まずはAPI専用のコントローラクラスを用意します。

app/controllers/api_controller.rb
class ApiController < ApplicationController
end

RABLが不要なAPI

追加/変更/削除等のレスポンスが非常にシンプルなAPIであれば、RABLを使う必要はありません。
その場合はコントローラ+モデルだけで書くことができます。
例としてユーザ名を変更するAPI、updateUserNameを定義してみます。

app/controllers/api_controller.rb
class ApiController < ApplicationController

  def updateUserName
    current_user.name = params[:name]

    if current_user.save
      render json: {result: true}
    else
      render json: {error: {code: Error::SAVE_FAILURE}}
    end
  end

end

ユーザ名の変更に成功すればresult:trueを返し、失敗した場合はエラーコードを返すようになっています。
この程度であればビューを括りだす必要はないですね。

RABLが便利なAPI

一覧取得や詳細取得などの情報表示に関わるAPIは、レスポンスが複雑になってきますので、
RABLを使ったビューを定義すると奇麗になります。

例としてアイテム一覧を取得するAPIを定義します。

app/controllers/api_controller.rb
class ApiController < ApplicationController

  def getItems
    @items = Item.all(:order => "created_at DESC")
  end

end

コントローラのアクションはこれだけです!

何をレスポンスに含めるのか、どういった構造で返すのか、
そういった情報はRABLビューで定義します。
RABLビューファイルは「アクション名.json.rabl」という名前にします。

app/views/api/getItems.json.rabl
collection @items, :root => :result, :object_root => false
attribute :id, :name

node(:photo_url) do |item|
  item.photo.url
end

このAPIは次のようなJSONレスポンスを返します。

getItemsレスポンス
{
  "result": [
    {
      "id": 1, "name": "Taro Yamada", "photo_url": "http://.../00001"
    },
    {
      "id": 2, "name": "Jiro Suzuki", "photo_url": "http://.../00002"
    }    
  ]
}

RABLのDSLによってJSONのレスポンスが簡潔に定義できているのがわかるかと思います。

もう少し複雑な例

もう1例としてアイテム詳細APIを定義してみます。

app/controllers/api_controller.rb
class ApiController < ApplicationController

  def getItemDetail
    @item = Item.find(params[:id])
  end

end
app/views/getItemDetail.json.rabl
object false

child(@item => :result) do
  attribute :id, :name, :memo, :bought_at, :location_name, :price,
    :user, :likes, :comments, :tags

  node :photo_url do |item|
    item.photo.url
  end

  child :user do
    attribute :id, :name

    node :photo_url do |user|
      user.photo.url
    end
  end

  child(:tags) do
    attribute :name
  end

  child(:likes) do
    attribute :user_id
  end

  child(:comments) do
    attribute :user, :body, :created_at

    child(:user) do
      attribute :id

      node :photo_url do |user|
        user.photo.url
      end
    end
  end
end

JSON出力結果は書くのが大変なので割愛しますが、
アイテムの中にコメントが複数あって、各コメントには書込んだユーザがいて、ユーザには写真があって、
といった複雑なレスポンスがわかりやすく記述できるようになります。

まとめ

RABLを使う事で情報表示系のAPIのメンテナンス性が格段に上がりました。
モバイルアプリやフルJavaScriptクライアント用にJSON APIを用意する時にはぜひ、おすすめです。

上記の例で出てくるRABL DSLの詳細については、この辺りをご参照ください。
http://nesquena.github.com/rabl/#usage

keyword
object, collection, child, attribute, node
134
133
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
134
133