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


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

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

フラットにする

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

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

```json
{
  "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`

```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のデータモデル](http://stateless.co/info-model.png)
図：HALのデータモデル

HALは現在[インターネットドラフト](https://tools.ietf.org/html/draft-kelly-json-hal-07)になっています。また、最近AWSのAPIでは積極的にHALが採用されています。

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


## JSON API

http://jsonapi.org/

Content-Type: `application/vnd.api+json`


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

```json
{
  "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` に入ります。それに加えて、リソースを識別するために `type` と `id` を使用します。

リソース間の関係を記述するため、特別に `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`

```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に特化したさまざまな仕様が用意されています。ぜひ採用を検討してみてはいかがでしょうか。

---

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

* HAL  
https://github.com/mikekelly/hal_specification/wiki/Libraries
* JSON API  
http://jsonapi.org/implementations/
* Collection+JSON  
（リンク集なし？ [検索する](https://www.google.co.jp/?q=%22collection%2Bjson%22+library)と多く見つかる）