0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Day12 — JSONレスポンスのベストプラクティス:成功・失敗・一覧・詳細の設計を統一する

0
Last updated at Posted at 2025-12-11

はじめに

Day11 では
HTTPステータスコードを正しく返す設計
を学びました。

今日はその続きとして、

「JSONレスポンスの中身をどう設計するのが正解なのか?」

を徹底的に解説します。

正直に言います。

❌ JSONレスポンスの設計がグチャグチャな API は
❌ フロントエンド開発が地獄になります。

今日でそれを完全に終わらせます。

今日のゴール

・成功・失敗で JSON を統一できるようになる

・一覧・詳細・作成・更新のレスポンス形式を揃えられる

・フロントエンドが「考えなくても」扱える API になる

・実務レベルのレスポンス設計ができるようになる

なぜ JSON レスポンス設計が重要なのか?

例えば、こんなレスポンスが混在していたらどうでしょう?

{ "result": "ok", "post": {...} }

{ "success": true, "data": {...} }

{ "status": "error", "msg": "失敗しました" }

フロント側は毎回こうなります。

if (res.result) { ... }
if (res.success) { ... }
if (res.status === 'error') { ... }

✅ 分岐地獄
✅ バグの温床

だからこそ JSON の形式は「全APIで統一」する必要があります。

結論:これが最強の JSON レスポンス設計

まずは これを全APIで固定してください。

成功時の基本フォーマット

{
  "status": "success",
  "data": {...}
}

失敗時の基本フォーマット

{
  "status": "error",
  "message": "エラーメッセージ"
}

たったこれだけです。
✅ これだけで フロント側が超書きやすくなります。

一覧取得(GET /posts)のレスポンス設計

{
  "status": "success",
  "data": [
    { "id": 1, "title": "最初の投稿" },
    { "id": 2, "title": "2つ目の投稿" }
  ]
}

✅ 配列は必ず data に入れる
✅ ページネーションも data を崩さずに追加できる

詳細取得(GET /posts/1)

{
  "status": "success",
  "data": {
    "id": 1,
    "title": "最初の投稿",
    "body": "本文です"
  }
}

✅ 一件でも必ず data に入れる
✅ 一覧と 構造を揃える

新規作成(POST /posts)

{
  "status": "success",
  "message": "投稿を作成しました",
  "data": {
    "id": 3,
    "title": "新しい投稿"
  }
}

✅ ユーザー向け文言 → message
✅ データ → data

更新(PUT /posts/1)

{
  "status": "success",
  "message": "投稿を更新しました",
  "data": {
    "id": 1,
    "title": "更新後タイトル"
  }
}

✅ 作成も更新も 構造は同じ

削除(DELETE /posts/1)

{
  "status": "success",
  "message": "投稿を削除しました"
}

✅ 削除後は data を返さなくてもOK

バリデーションエラー(400)

{
  "status": "error",
  "message": "タイトルは必須です"
}

認証エラー(401)

{
  "status": "error",
  "message": "ログインが必要です"
}

存在しないデータ(404)

{
  "status": "error",
  "message": "投稿が見つかりません"
}

サーバエラー(500)

{
  "status": "error",
  "message": "サーバ側で問題が発生しました"
}

Laravel で JSON フォーマットを統一する実装例

成功レスポンス

return response()->json([
  'status' => 'success',
  'data' => $post
], 200);

エラーレスポンス

return response()->json([
  'status' => 'error',
  'message' => '投稿が見つかりません'
], 404);

フロントエンド(React)側はこう書ける

fetch('/api/posts')
  .then(res => res.json())
  .then(json => {
    if (json.status === 'success') {
      setPosts(json.data);
    } else {
      alert(json.message);
    }
  });

✅ 成功でも失敗でも
同じ分岐ロジックがすべてのAPIで使い回せます

JSON 設計で初心者が必ずやるミス

APIごとにレスポンス構造が違う

{ "post": {...} }
{ "posts": [...] }
{ "item": {...} }

✅ 全部 data に統一する

エラーメッセージが配列だったり文字列だったりする

{ "message": ["必須です"] }
{ "message": "必須です" }

✅ メッセージの型も必ず揃える

ステータスと JSON の意味がズレている

HTTP 200
{
  "status": "error"
}

✅ HTTPステータスと JSON の意味は一致させる

今日のまとめ

・JSONレスポンスは 全APIで統一する

・成功:{ status: "success", data: ... }

・失敗:{ status: "error", message: "..." }

・一覧・詳細・作成・更新はすべて同じ構造でOK

・フロント側は status だけ見ればよくなる

・APIが「チームで使える設計」に進化する

次回 Day13 は…

「ミドルウェアでリクエストを加工する:認証・ログ・バリデーションの入口」

これでいよいよ
「誰が・いつ・どのリクエストを送ってきたのか
を制御できるようになります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?