delhi09の勉強日記

技術トピック専用のブログです。自分用のメモ書きの投稿が多いです。あくまで「勉強日記」なので記事の内容は鵜呑みにしないでください。

JSONを返すAPIの共通部分(=エンベロープ)について

概要

JSONを返すAPI実装するときに、以下のように共通部分でラップしたレスポンスを返すことがある。

{
    "header": {
        "code": "0",
        "message": "success"
    },
    "result": {
        "books": [
            {
                "id": 1,
                "name": "技術書1",
                "price": 3000
            },
            {
                "id": 2,
                "name": "技術書2",
                "price": 1500
            }
        ]
    }
}

※ 「共通部分でラップ」とは以下の部分のこと

"header": {
    "code": "0",
    "message": "success"
},
"result": {
    # ここに返したいデータを入れる
}

実装する側になったときに共通部分のことを考えるのが地味に面倒なのと、そもそもこれって要るのかな?って前から気になっていたので調べてみた。

「共通部分」の呼び方について

Web API The Good Parts』によると、この共通部分のことは「エンベロープ(=封筒)」と呼ぶらしい。

エンベロープとは日本語で言うと「封筒」を意味しますが、APIのデータ構造の文脈で言えば、すべてのデータ(レスポンスやリクエスト)を同じ構造でくるむことを言います。
...
メタデータも含んだような形ですべてのAPIが同じデータ構造を前すために実際のデータをくるむための構造をエンベロープと呼びます。

『Web API The Good Parts』 p78〜p79より


たしかに、「API エンベロープ」とか「API envelope」で検索すると情報がたくさん出てくる。

そもそもAPIの共通部分のことを「エンベロープ」って呼ぶっていう知識がないとこの手の議論にアクセスできないんだなと思った。(私は今まで知らなかった。)

エンベロープ」は必要なのか

『Web API The Good Parts』には「エンベロープ」はメタ情報を扱えるので一見便利に見えるが、以下の理由でやるべきではないと記載されていた。

  1. HTTP通信ではHTTPヘッダーが「エンベロープ」の役割を果たす。
  2. 1.より「エンベロープ」は冗長である。

言われてみれば、以下のようにHTTPステータスコードに応じてレスポンスを返せば不要な気がする。

200のとき

{
    "books": [
        {
            "id": 1,
            "name": "技術書1",
            "price": 3000
        },
        {
            "id": 2,
            "name": "技術書2",
            "price": 1500
        }
    ]
}

400のとき

{
    "code": "400-1",
    "message": "invalid parameter xxx."
}

※ 200系の場合と400系や500系の場合でJSONスキーマが異なると、クライアント側でJSONをデシリアライズするときに困るのでは?と思うかもしれないが、クライアント側がそれぞれのステータスコードの場合のJSONスキーマを知っていれば問題ない。

参照系以外のAPIの場合はどうするのか?

次に気になったのは参照系(=CRUDのR以外)の場合はどうするのかということだった。

必要がなければ何も返す必要がないのだが、APIでレスポンスボディに何も返さないというのは、個人的には感覚として気持ち悪い気もする。

調べてみたところ、例えば以下の記事ではC(作成)については記述がなかったが、

  1. U(更新): 更新後のオブジェクトを返す。
  2. D(削除): 204 No Contentを返す。

ことを提案していてなるほどと思った。

medium.com

また、RFCも読んでみた。
https://datatracker.ietf.org/doc/html/rfc2616#section-9.5

RFC2616によると、以下が推奨されていた。

  • POSTで新規作成した場合: 201 Createdを返して作成されたリソースを参照できるもの(恐らく主キーのなど)を返す。
  • PUTで更新した場合: 200204 No Contentを返す。
  • DELETEで削除した場合: 200202 Accepted(削除処理が非同期の場合)か204 No Contentを返す。

自分がAPIを実装するときは、正常なときは固定で200を返してしまうことが多くて、201 Createdとか202 Acceptedとか204 No Contentとか200系のHTTPステータスコードを細かく使い分けたことがなかったので、なるほどと思った。

いったん以上