RailsのViewでJbuilderを使用していて、collectionのレンダリングが遅いなと感じたことはないでしょうか?
その場合、jsonapi-serializer で手軽に改善できるかもしれません。
jsonapi-serializer の詳しい使い方は 公式ドキュメント を見ていただくとして、本記事では Jbuilder を jsonapi-serializer で置換する際のTipsを書きます。
なお完全に置換するのではなく手軽に検証するために Jbuilder と jsonapi-serializer を共存させるハイブリッド形式にしています。(効果が確認でき必要であれば全置換すればいいと思います)
おことわり
とある事情により本記事を書いており、主に時間の制約により以下の注意点があります。
- 実際のプロジェクトで以前に対応した内容を基に本記事を書いています。結構速くなった記憶はあるのですが、何倍速くなったとか、実際のベンチマークを提示することはできません。
- 本記事中のコードは動作確認していません。雰囲気だけ参考にしてください。
jsonapi-serializer とは
データをJSON APIフォーマットで出力してくれるgemで、速さをウリにしています。
もとは Netflix/fast_jsonapi で開発されていましたが、メンテナンスされなくなったため有志コミュニティにより fork されました。それからアレコレあり現在の jsonapi-serializer/jsonapi-serializer になりました。
Tips
Serializer Moduleを作成する
先述の通り jsonapi-serializer の出力は JSON API の仕様で定められたフォーマットとなり、jbuilder を置換するには使いにくいです。
そのため以下のような Serializer Moduleを作成します。
module ApplicationSerializer
extend ActiveSupport::Concern
included do
include JSONAPI::Serializer
end
class_methods do
# 短く書きたい場合は以下のようなクラスメソッドがあるといいかもしれません
# def attributes(...)
# new(...).attributes
# end
end
# 必要な部分のみを抽出
def attributes
data = serializable_hash[:data]
case data
when Hash
data[:attributes]
when Enumerable
data.pluck(:attributes)
else
data
end
end
end
Jbuilder の show.json.jbuilder など単一リソースに対応する Serializer を作成する
たとえば以下のような感じです。
json.extract! @post, :id, :title, :content, :published_at
class PostSerializer
include ApplicationSerializer
attributes :id, :title, :content, :published_at
end
show.json.jbuilder の中身を Serializer を使用するように変更する
json.merge! PostSerializer.new(@post).attributes
同様に index.json.jbuilder など複数リソースのviewを Serializer を使用するように変更する
json.array! @posts, partial: 'posts/post', as: :post
↓
json.merge! PostSerializer.new(@posts).attributes
(Serializerに渡すものは単一・複数リソースのどちらでもOKです)