Edited at
Web APIDay 5

Web APIにはJSONベースのフォーマットを使おう

More than 3 years have passed since last update.

Web APIを作るとき、JSONのデータ構造をどうするか悩んだことはありますか?

{

"id": 3342124,
"message": "Hi!",
"user": {
"id": 3456,
"name": "Taro Yamada",
"image_url": "/images/taro.png"
}
}

フラットにする

{

"id": 3342124,
"message": "Hi!",
"user_id": 3456,
"user_name": "Taro Yamada",
"user_image_url": "/images/taro.png"
}

エンベロープでデータ全体を包む

{

"response": {
"id": 3342124,
"message": "Hi!",
"user": {
"id": 3456,
"name": "Taro Yamada",
"image_url": "/images/taro.png"
}
}
}

など、どの構造がいいでしょうか? もっと違う構造も考えられます。

JSONはシンプルですが、構造に制約がなさすぎます。適切な設計を行うには適切な制約が必要です。

そこで、plain JSONに少し制約を加えたJSONベースのフォーマットを使うことをおすすめします。


もしあなたが、JSONレスポンスをどのようなフォーマットにするかをチームで議論したことがあるなら、JSON APIは『自転車置き場の議論』に対抗する武器となる。

共有された規約に従うことで、生産性が向上し、汎用的なツールを利用でき、アプリケーションという重要なものに集中することができる。


http://jsonapi.org/ より

制約の弱いもの、強いもの、さまざまなフォーマットが存在しますが、今回は比較的制約の強いフォーマットを紹介します。


HAL

http://stateless.co/hal_specification.html

Content-Type: application/hal+json

{

"_links": {
"self": { "href": "/messages/3342124" }
},
"id": 3342124,
"message": "Hi!",
"_embedded": {
"user": {
"_links": {
"self": { "href": "/users/3456" }
},
"id": 3456,
"name": "Taro Yamada",
"image_url": "/images/taro.png"
}
}
}

通常のオブジェクト(リソースと呼ぶ)に _links _embedded というプロパティを加えたものになっています。この _embedded の中にさらにリソースを入れられる(埋め込みリソース)ようになっているので、ただネストするよりも、複数のリソース間の関係がはっきりします。

HALのデータモデル

図:HALのデータモデル

HALは現在インターネットドラフトになっています。また、最近AWSのAPIでは積極的にHALが採用されています。

例: http://docs.aws.amazon.com/apigateway/api-reference/


JSON API

http://jsonapi.org/

Content-Type: application/vnd.api+json

紛らわしいですが、“JSON API”というフォーマットの名前です。

{

"links": {
"self": "/messages/3342124"
},
"data": {
"type": "messages",
"id": 3342124,
"attributes": {
"message": "Hi!"
},
"relationships": {
"user": {
"data": {
"type": "users",
"id": 3456
}
}
}
},
"included": [{
"links": {
"self": { "href": "/users/3456" }
},
"type": "users",
"id": 3456,
"attributes": {
"name": "Taro Yamada",
"image_url": "/images/taro.png"
}
}]
}

JSON APIでは、従来のデータ(キー・値)は data の中の attributes に入ります。それに加えて、リソースを識別するために typeid を使用します。

リソース間の関係を記述するため、特別に relationships が用意されています。また、埋め込みリソースは included で可能です。

さらに、JSON APIの特徴として、以下のようにかなり仕様が豊富です。


  • GETだけではなく、POST/PATCH/DELETE(いわゆるCRUD操作)の仕様を定義している

  • 特定のフィールドのみを指定したり、コレクションのsort、page、filterを行うURLパラメータの仕様を定義している

  • エラー時のフォーマットを定義している


Collection+JSON

http://amundsen.com/media-types/collection/

Content-Type: application/vnd.collection+json

{

"collection": {
"version": "1.0",
"href": "/messages",
"items": [{
"href": "/messages/3342124",
"data": [
{ "name": "id", "value": 3342124 },
{ "name": "message", "value": "Hi!" }
],
"links": [
{ "rel": "user", "href": "/users/3456" }
]
}],
"template": [{
"data": [
{ "name": "message", "value": "", "prompt": "Enter message" }
]
}],
"queries": [{
"rel": "search",
"href": "/messages/search",
"prompt": "Search messages",
"data": [
{ "name": "q", "value": "" }
]
}]
}
}

Collection+JSONは基本的にコレクション(複数のリソースをまとめたもの)を扱うフォーマットです。データは collection の中の items に配列として入ります。配列の中身を1つだけにすることで単独のリソースも表すことができます。

埋め込みリソースは標準では用意されていません(拡張仕様で可能)。

Collection+JSONの一番の特徴は template queries です。これはHTMLのフォームのようにリクエスト時のパラメータを指定します。 template はリソースに対するPOST/PUT/DELETE操作のとき、 queries はそれ以外の検索などの操作に使用します。クライアントはこの記述に従うことでサーバに正しいクエリのリクエストを送信することができます。

また、エラー時のフォーマットも定義されています。


フォーマットを使うメリット


  • データ構造の設計に悩まなくてよくなる

  • クライアント・サーバの実装にそのフォーマット用のライブラリを利用できる

  • フォーマットの仕様がデータ構造(場合によってリクエストパラメータなども)の仕様になるので、その部分についてはドキュメントを書く必要がなくなる

うまくライブラリを使えば、作業が大幅に省力化できるでしょう。

JSONベースのフォーマット自体がまだマイナーなので、必要なプラットフォームのライブラリがまだないかもしれませんが、汎用的な仕様なので、一度ライブラリを書けば以後も再利用することができます。

今回紹介したものはWeb APIのための汎用的なフォーマットなので、Web APIに特化したさまざまな仕様が用意されています。ぜひ採用を検討してみてはいかがでしょうか。


参考:ライブラリのリンク集