Railsでjsonapi-serializer使ってみた
はじめに
api仕様を決めるのに苦労していませんか?
今回は規則的にapiの型等を決定できる手法をご紹介します。
json-api-serializerとは
jsonapi-serializerは、RubyにおけるJSON:APIのserializerのことです。
特徴は以下の通り。
- Active Model Serializerと似た宣言構文
- belongs_to, has_many, has_oneというrelationsをサポート
- includedを利用して、複合的な文書をサポート
- 複合的な文書の最適化されたシリアライゼーション
- キャッシング
JSON:APIとは
apiを設計する際のJSONフォーマットを定めたものです。
例えば、以下のようなものです。
{
"links": {
"self": "http://example.com/articles",
"next": "http://example.com/articles?page[offset]=2",
"last": "http://example.com/articles?page[offset]=10"
},
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON:API paints my bikeshed!"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
},
"data": { "type": "people", "id": "9" }
},
"comments": {
"links": {
"self": "http://example.com/articles/1/relationships/comments",
"related": "http://example.com/articles/1/comments"
},
"data": [
{ "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
]
}
},
"links": {
"self": "http://example.com/articles/1"
}
}],
"included": [{
"type": "people",
"id": "9",
"attributes": {
"firstName": "Dan",
"lastName": "Gebhardt",
"twitter": "dgeb"
},
"links": {
"self": "http://example.com/people/9"
}
}, {
"type": "comments",
"id": "5",
"attributes": {
"body": "First!"
},
"relationships": {
"author": {
"data": { "type": "people", "id": "2" }
}
},
"links": {
"self": "http://example.com/comments/5"
}
}, {
"type": "comments",
"id": "12",
"attributes": {
"body": "I like XML better"
},
"relationships": {
"author": {
"data": { "type": "people", "id": "9" }
}
},
"links": {
"self": "http://example.com/comments/12"
}
}]
}
元々は、fast_jsonapiと呼ばれていたものをアップデートして受け継いだものです。
fast_jsonapiとは
fast_jsonapiとは、NetflixがOSSとして開発していたgemでJSONのserializerです。
fastとついているほどなので、高速なレスポンスを提供しているようです。
現在はこのプロジェクトは止まっており、全て上記のjson_serializerに継承されています。
ところでserializeとは
IT用語辞典によると、
シリアライズとは、複数の要素を一列に並べる操作や処理のこと。単にシリアライズといった場合には、プログラムの実行状態や複雑なデータ構造などを一つの文字列やバイト列で表現する「直列化」を指すことが多い。
らしいです。
今回の文脈におけるserializeは、あるデータを一定の規則に沿って整形することを指します。
railsの例で言うと、Modelの構造(attirbutesやrelations)に沿って決まりきった形式でデータを整形することを表します。
使い方
前提
次のような三つのモデルがあったとします。
- 学校
- 生徒
- 科目
# app/models/school.rb
class School < ActiveRecord::Base
has_many :students
end
# app/models/student.rb
class Student< ActiveRecord::Base
belong_to :school
has_many :subjects
validates :name, presence: true
end
# app/models/subject.rb
class Subject< ActiveRecord::Base
belong_to :student
end
gemをインストール
Gemfileに以下追記。
# Gemfile
gem 'jsonapi-serializer', '~> 2.2'
Serializerを記述
次のポイントに気を付けて、記述。
- relations(has_many, had_one, belongs_to)
- attributes
仮に StudentのSerializerを作成する場合は、以下のように記述してください。
# app/serializers/student_serializer.rb
class StudentSerializer
include JSONAPI:Serializer
belongs_to :school, lazy_load_data: true, serializer: SchoolSerializer
has_many :subjects, lazy_load_data: true, serializer: SubjectSerializer
attributes :name
end
もちろん、Studentの関係性をもつモデルのSerializerも必要になります。
使ってみた感想
「実装コスト」「可読性」「パフォーマンス」が改善されました。
特に、「実装コスト」が大幅に改善され、圧倒的に楽になりました。
今までは、毎度apiを作成する際にjsonフォーマットを決めていたので、かなり苦労していましたが、そのコストが0になったという印象。
さらに、Model通りのrelations, attributesをapiとして返すことが可能ですので、全体的に統一感が出ました。ソースコードとしても、かなり規則的でリーダブルなものになったと思います。
一方で、apiを使用する側はparseするのが少し大変そうと思ったり…。