API レスポンスはどんな構造にすべきか
要点:裸のオブジェクトはメタデータが必要になるまで綺麗で、完全なエンベロープは安全だが冗長です。ほとんどのプロジェクトでは結果に HTTP ステータス、データに安定したエンベロープを使います。

要点:結果は 4xx/5xx ステータスコードで表し、ペイロードを { "data": ..., "meta": ... } で運び、失敗時は error エンベロープに切り替えましょう。内部 API にはこれで十分で、多くの統合者を持つ公開プラットフォームだけが JSON:API や OpenAPI を必要とします。
最初に最悪の選択肢を除外します。常に 200 を返し、結果を code フィールドに入れること。
三つの形
A. 裸のオブジェクト
{ "id": 1, "name": "demo", "price": 99 }
綺麗で、HTTP の意味論が仕事をします(404 は 404)。しかしページネーション、警告、トレース ID が必要になった瞬間、契約を変えずに置く場所がありません。
B. 安定したエンベロープ
{
"data": { "id": 1, "name": "demo" },
"meta": { "requestId": "abc", "took": 12 }
}
ペイロードとメタデータが分かれて住むので、新しいメタデータが data を再形成しません。コストはネスト一段です。
C. 全部エンベロープ(避ける)
{ "code": 0, "message": "success", "data": {...} }
形が罪ではなく、慣行が罪です。すべての結果に 200 を返すと、CDN、ゲートウェイ、サーキットブレーカー、リトライポリシー、ダッシュボードがすべて成功を見ます。すべてのアラートが独自パースを必要とします。
推奨
結果は HTTP ステータス、ペイロードはエンベロープ。
{
"data": { "id": 1, "name": "demo" },
"meta": { "requestId": "req_8f2a", "took": 12 }
}
{
"error": {
"code": "ORDER_NOT_FOUND",
"message": "注文が存在しないか削除されました",
"details": [{ "field": "id", "issue": "not_found" }]
},
"meta": { "requestId": "req_8f2a" }
}
ステータスは 4xx か 5xx。code は安定して機械可読。message は人間向け。details はフィールド単位のフォームエラー用。
厳格な規則
- リストエンドポイントはページネーションのメタデータを運ばなければならない。
totalかカーソルがなければページャーがありません。 - 空のリストは
[]を返し、決してnullにしない。null.mapは繰り返し起きる障害です。 - タイムスタンプはオフセット付き ISO 8601。 タイムスタンプはどの形式かを参照。
- 命名規則は一つ。
snake_caseかcamelCase、境界で一度だけ変換。 - 金額は整数の最小単位か文字列。
99.99は二進浮動小数点では正確でない。 nullと欠落を混同しない。nullかフィールド省略かを参照。
仕様を採用すべきとき
- 多くの統合者を持つ公開プラットフォーム:JSON:API か OpenAPI と共有エンベロープ
- 単一チームの内部 API:上のエンベロープで十分
- 強い契約:Schema 優先で、構造はそこから生成
仕様は議論を節約し、オンボーディングのコストを払います。五人チームでは、JSON:API は通常マイナスの仕事です。
FAQ
裸のオブジェクトとエンベロープはどう選べばよいですか?
ページネーション、警告、requestId が必要ならエンベロープを使い、data を再形成せずに meta を育てられるようにします。メタデータが不要で、後からフィールドを足すと契約が変わることを受け入れられるなら、裸のオブジェクトを返します。
API がすべての結果に 200 を返してはならないのはなぜですか?
CDN、ゲートウェイ、サーキットブレーカー、リトライポリシー、ダッシュボードは HTTP ステータスを読んでキャッシュ、リトライ、アラートを決めます。どこでも 200 だと失敗を成功として扱い、すべてのアラートが業務コードの独自パースを必要とします。
リストエンドポイントはページネーションのメタデータを運ぶべきですか?
はい。total かカーソルがなければページャーがなく、空のリストは null ではなく [] を返さなければ、フロントエンドの .map 呼び出しが壊れます。