よくわからなかったので整理してみる。
候補
- Jbuilder https://github.com/rails/jbuilder
- ActiveModelSerializers https://github.com/rails-api/active_model_serializers
- jsonapi-rb https://github.com/jsonapi-rb/jsonapi-rb
- JSON:API Serializer https://github.com/jsonapi-serializer/jsonapi-serializer
- Blueprinter https://github.com/procore/blueprinter
- Jb https://github.com/amatsuda/jb
tl;dr
基本はjbuilderでいいと思う。 パフォーマンスが気になるならActiveModelSerializersか、Jb。ただ、どちらにせよメンテは心配。
Jbuilder
- https://github.com/rails/jbuilder
- HEYのGemfile見る限り、DHHは使っているようだ
- テンプレート評価が遅いとか、パーシャル使うとpartの評価でN+1問題とか、パフォーマンス関連の課題があったみたいだけどどうなんだろう。コード見る限り変わってないような。
- 開発は継続されている
サンプルコード
json.extract! @post, :id, :title, :content, :published_at json.author do if @post.anonymous? json.null! # or json.nil! else json.first_name @post.author_first_name json.last_name @post.author_last_name end end
ActiveModelSerializers
- https://github.com/rails-api/active_model_serializers
- Star: 5000
- masterは2018年から開発が止まっているけど、
0-10-stable
ブランチは年初にRuby2.7のサポートが入っている - Starも多し定番だった。メンテの頻度下がってるけど今も使ってる人は多いと思う
サンプルコード
class PostSerializer < ActiveModel::Serializer attributes :title, :body has_many :comments has_one :author end
シリアライザは自動で適用されるのでコントローラは変更不要。
class PostsController < ApplicationController def show @post = Post.find(params[:id]) render json: @post end end
jsonapi-rb
- https://github.com/jsonapi-rb/jsonapi-rb
- Star: 214
- 2017年から開発が止まっている
- JSON APIシリアライザなのでそれに乗っかれるか
サンプルコード
class SerializablePost < JSONAPI::Serializable::Resource type 'posts' attributes :title, :body attribute :date do @object.created_at end belongs_to :author has_many :comments do data do @object.published_comments end end end
あんまり使いやすそうな気がしない。
JSON:API Serializer
- https://github.com/jsonapi-serializer/jsonapi-serializer
- Star: 282
- もともと netflix/fast_jsonapi だったのがフォークされ改名された
- わりあい安定してメンテされている
- JSON APIシリアライザなのでそれに乗っかれるか
サンプルコード
class MovieSerializer include JSONAPI::Serializer set_type :movie # optional set_id :owner_id # optional attributes :name, :year has_many :actors belongs_to :owner, record_type: :user belongs_to :movie_type end
json_string = MovieSerializer.new(movie).serializable_hash.to_json
{ "data": { "id": "3", "type": "movie", "attributes": { "name": "test movie", "year": null }, "relationships": { "actors": { "data": [ { "id": "1", "type": "actor" }, { "id": "2", "type": "actor" } ] }, "owner": { "data": { "id": "3", "type": "user" } } } } }
Blueprinter
- https://github.com/procore/blueprinter
- Star: 474
- わりあい安定してメンテされている
- Rubyでシリアライザを書くタイプ
サンプルコード
# app/blueprints/todo_blueprint.rb class TodoBlueprint < Blueprinter::Base identifier :id view :normal do fields :name, :due_at, :completed_at end view :extended do include_view :normal fields :description, :created_at, :updated_at end end
# app/controllers/api/todos_controller.rb module Api class TodosController < ApplicationController def index todos = TodoBlueprint.render Todo.all, view: :normal render json: todos end def show todo = TodoBlueprint.render Todo.find(params[:id]), view: :extended render json: todo end end end
[ { "id":1, "completed_at":null, "due_at":"2018-03-01 23:09:53 UTC", "name":"todo0" }, { "id":2, "completed_at":null, "due_at":"2018-03-02 23:09:53 UTC", "name":"todo1" }, ... ]
Jb
- https://github.com/amatsuda/jb
- Star: 948
- わりあい安定してメンテされているが、amatsudaが一人でメンテしている感じ。個人のネームスペースだし
- jbuilderみたいにビューファイルを書くタイプ
- なので乗り換えは割とやりやすい
サンプルコード
# app/views/messages/show.json.jb json = { content: format_content(@message.content), created_at: @message.created_at, updated_at: @message.updated_at, author: { name: @message.creator.name.familiar, email_address: @message.creator.email_address_with_name, url: url_for(@message.creator, format: :json) } } if current_user.admin? json[:visitors] = calculate_visitors(@message) end json[:comments] = @message.comments.map do |comment| { content: comment.content, created_at: comment.created_at } end json[:attachments] = @message.attachments.map do |attachment| { filename: attachment.filename, url: url_for(attachment) } end json
JSON APIどうなのか
ページングとかの表現とか、みんなバラバラになるから大統一フォーマットを作ろうというので制定されている。 気持ちはわからないでもないけど、この汎用的な、冗長な表現が天下をとるとも思えないんだよなあ。 見れば見るほど気持ちはわかるんだけど、もっと気軽にやりたいんだという。実は僕が知らないだけで、みんなこれ使ってたりする?
こういうレスポンスになる。
{ "data": { "id": "3", "type": "movie", "attributes": { "name": "test movie", "year": null }, "relationships": { "actors": { "data": [ { "id": "1", "type": "actor" }, { "id": "2", "type": "actor" } ] }, "owner": { "data": { "id": "3", "type": "user" } } } } }