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?

【HTTP】更新エンドポイントのレスポンスは200 OKと204 No Contentどっちが正しい?

0
Posted at

はじめに

先日、PATCHエンドポイントの設計書を書いてレビューに出したところ、こんなコメントをもらいました。

「更新は200 OKではなく204 No Contentの認識です」

「え、そうなの?」となりまして。正直なところ「更新成功したんだから200でいいでしょ」くらいの認識でいたので、自信を持って反論もできず。

せっかくなので調べることにしました。調べるうちに「そもそもHTTPメソッドとステータスコードについてきちんと整理できていなかった」という反省もあったので、基礎から一緒にまとめます。

HTTPメソッドの基本

HTTPメソッドとは、クライアントがサーバーに「どんな操作をしたいか」を伝えるための命令です。URLが「どこに」アクセスするかを示すなら、メソッドは「何をするか」を示します。

主なメソッド

メソッド 操作 特徴
GET 取得 サーバーの状態を変えない
POST 新規作成 ボディにデータを乗せて送る
PUT 完全置き換え リソース全体を上書きする
PATCH 部分更新 変えたいフィールドだけ送る
DELETE 削除

PUTとPATCHの違い(ここ混乱しがち)

この2つは似ているようで、動作が全然違います。実際のデータで確認してみます。

# 現在の状態
{ "name": "田中", "email": "tanaka@example.com", "age": 30 }

PUTでnameだけ送ると、送らなかったフィールドが消えます。

// PUT /users/1
// Body: { "name": "田中太郎" }

// → { "name": "田中太郎", "email": null, "age": null }

PATCHでnameだけ送ると、他のフィールドはそのまま残ります。

// PATCH /users/1
// Body: { "name": "田中太郎" }

// → { "name": "田中太郎", "email": "tanaka@example.com", "age": 30 }

PUTは「全部送る前提」、PATCHは「変えたいところだけ送る」という使い分けです。一部だけ更新したいのにPUTを使うと、送らなかったフィールドが消えてしまうので注意が必要です。

冪等性という概念

メソッドを整理するうえで「冪等性(べきとうせい)」という概念を知っておくと理解が深まります。

同じリクエストを何回繰り返しても、結果が変わらない性質のことです。

メソッド 冪等 補足
GET 何回取得しても同じ
PUT 何回上書きしても同じ状態になる
DELETE 2回目以降は「もう存在しない」で変わらない
POST 叩くたびに新しいリソースが作られる
PATCH 非冪等。ただし冪等になるよう設計することも可能

これが実務でどう関係するかというと、ネットワーク障害でリトライが発生したときです。POSTをリトライすると二重登録になる、というのがよくある事故パターンです。「冪等かどうか」はリトライ処理の設計にも影響するので、頭に入れておくと便利です。

なお、PATCHはRFC 5789で「非冪等」と定義されていますが、「updated_atを特定の値で上書きする」といった実装にすれば冪等にすることもできます。設計によって変わるというより、デフォルトは非冪等だと理解しておくのが正確です。


ステータスコードの基本

ステータスコードはレスポンスに含まれる3桁の数字で、「リクエストの結果がどうだったか」をクライアントに伝えます。

人間が読むためだけでなく、ブラウザやHTTPクライアント、ミドルウェアが機械的に判断するために使うという側面があります。

分類 意味
2xx 成功
4xx クライアント側のエラー
5xx サーバー側のエラー

私の現場ではこの3系統をよく使います。


2xxの使い分け

200 OK

最も汎用的な成功コードです。GETやPATCHのレスポンスによく使います。

201 Created

リソースが新しく作られたときに返します。POSTで新規作成に成功したら200ではなく201を返すのが正しいです。仕様上、作成したリソースのURLをLocationヘッダーに含めることが求められています(リクエストURLとリソースのURLが一致する場合は省略可能)。

POST /users → 201 Created
Location: /users/123

204 No Content

成功したが、返すボディがないときに使います。DELETEや一部のPATCHで使います。

DELETE /users/1 → 204 No Content(ボディなし)

本題:PATCHは200か204か

ここが今回調べようとしたメインテーマです。

結論から言うと、どちらも間違いではなく、状況によって使い分けるものでした。

判断基準は「サーバーが自動で値を変えるかどうか」

200を返す場合

サーバー側で自動的に変更されるフィールドがある場合は、更新後のリソースをボディに入れて200で返します。

たとえばupdated_atなどのタイムスタンプはサーバーが自動で付けます。この値をクライアントに伝えないと、クライアントは正確な状態を知るために別途GETを叩く必要が出てきます。

// PATCH /users/1
// Body: { "name": "田中太郎" }

// 200 OK
{
  "id": 1,
  "name": "田中太郎",
  "email": "tanaka@example.com",
  "updated_at": "2026-06-18T10:00:00Z"  // サーバーが自動で付けた値
}

204を返す場合

送った内容がそのまま保存されるだけで、サーバー側での追加変更が何もない場合です。「自分が送ったものが保存されたことは自明なので、わざわざ返すものがない」という判断です。

PATCH /users/1
Body: { "name": "田中太郎" }

→ 204 No Content(ボディなし)

整理すると**「サーバーが勝手に何かを変えるなら200、そうでないなら204」**です。

レビューコメントの意図を考えると

「更新は204」というコメントは間違いではありませんが、updated_atのような自動更新フィールドがある場合は200の方がクライアントに親切な設計になります。

おそらく「更新エンドポイントにとりあえず200を返してボディを返す設計」への指摘だったと思われます。自動更新フィールドがないなら204で十分、という認識のすり合わせが必要でした。設計書に書くなら「更新後のリソースを返す場合は200、返さない場合は204」という形で根拠まで書いておくと、こういった認識ズレが防げそうだと感じました。


ついでにPUTの場合も整理した

PATCHと基本的な考え方は同じです。ただ、PUTならではの事情が1つあります。

PATCHは変えたいフィールドだけ送るため、クライアントは「更新後の全体像」を自分では把握できません。だから200でリソースを返す意味が大きいです。

一方PUTはリソース全体を送っているため、サーバーが自動変更するフィールドがなければ「自分が送ったものがそのまま保存された」とクライアントが自分で把握できます。なのでPUTはPATCHより204を返しやすい状況になりやすいです。

加えて、PUTには「指定したURLにリソースがなければ作る(upsert)」という使い方があります。その場合は201を返します。

PUT /users/999  ← ID=999がまだ存在しない場合は新規作成として扱う

→ 201 Created(新規作成された場合)
→ 200 or 204(既に存在していて更新された場合)

ついでに4xxも整理しておく

調べるついでに、4xx系もきちんと整理しました。普段なんとなく使っていたものの、いざ設計書に書こうとすると「これ400で合ってる?」と迷う場面がありました。

400 Bad Request

リクエストの形式や値がおかしいとき。バリデーションエラーはここです。

401 / 403(ここが混乱しやすい)

401 Unauthorized → 「あなたが誰かわからない」(未認証)
403 Forbidden    → 「あなたが誰かはわかるが、ここには入れない」(権限なし)

401はログインしていない・トークンが無効・期限切れなど。403はログイン済みだが他人のデータにアクセスしようとしたケースです。名前が「Unauthorized(認可されていない)」なのに実態は「認証エラー」なのがわかりにくいですが、意味は全然違います。

404 Not Found

リソースが存在しないとき。「存在すること自体を隠したい」場合(他人のプロフィールが非公開など)は意図的に404を返すこともあります。

409 Conflict

リソースの競合。メールアドレスの重複登録、楽観的ロックの衝突など。400と迷いがちですが、使い分けの基準は「リクエスト自体は正しいが、現在の状態と衝突しているかどうか」です。

POST /users
Body: { "email": "すでに登録済みのアドレス" }
→ 409 Conflict(400ではない)

まとめ

  • PATCHで200か204かは「サーバーが自動で変える値があるかどうか」で判断する
    • updated_atなどの自動更新フィールドがあるなら200でリソースを返す方がクライアントに親切
    • 送った通りそのまま保存されるだけなら204で十分
  • PUTも同じ考え方。加えてupsertになる場合は201を返す
  • 401と403は別物。「未認証」と「権限なし」を混同しない
  • 重複登録のような競合は400ではなく409

レビューのコメントで「なんとなく200でいいでしょ」という認識が間違っていたことがわかりました。同時に、設計書には「なぜそのステータスコードを選んだか」の根拠まで書いておくと、認識ズレを防げるなと思いました。

参考になったら いいねストック をお願いします!
同じような経験をされた方のコメントもお待ちしています。

参考

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?