DELETE API設計:既に削除済みユーザーに204を返すか404を返すか
問題¶
DELETE /users/1 Should it return 204 or 404, if the user is already deleted?
解答¶
どちらも正解であり得る。ただし実務では 204 を推奨するケースが多い。
解説¶
204 No Content を返す場合(冪等性重視)¶
理由: - HTTP の DELETE メソッドは冪等(idempotent)であるべき - 「ユーザー1が存在しない」という最終状態は、1回目も2回目も同じ - クライアントは「削除できた」として安全に処理を続けられる - 分散システムやリトライ処理でのエラーハンドリングがシンプルになる
404 Not Found を返す場合(厳密な意味論重視)¶
理由: - 「存在しないリソースへの操作」を明示したい - クライアントが「本当に削除したか」vs「そもそも存在しなかったか」を区別できる - REST の純粋な意味論(リソースが見つからない = 404)に準拠
RFC 9110 の立場¶
HTTP 仕様(RFC 9110)は DELETE の冪等性を定義しているが、ステータスコードの選択は実装次第と述べている。ただし「意図した最終状態が達成されている場合は 2xx が適切」というニュアンスがある。
実務での判断基準¶
┌─────────────────────────────────────────────────────────┐
│ リトライ・冪等性が重要(分散システム、モバイルアプリ) │
│ → 204 を推奨 │
│ │
│ クライアントが「削除したか / 元々なかったか」を区別する必要 │
│ → 404 を返してもよい │
│ │
│ 監査ログや論理削除がある場合 │
│ → 論理削除 → 204(物理的には存在する) │
└─────────────────────────────────────────────────────────┘
コード例(Go)¶
func deleteUser(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
err := db.DeleteUser(id)
// 冪等性重視: 既に削除済みでも 204
if err == ErrNotFound {
w.WriteHeader(http.StatusNoContent) // 204
return
}
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusNoContent) // 204
}
Stripe・GitHub などの有名 API の事例¶
| サービス | DELETE 後の挙動 |
|---|---|
| Stripe | 削除済みオブジェクトは { "deleted": true } を 200 で返す |
| GitHub | 存在しないリソースには 404 |
| Twilio | 冪等性を明示し、2回目の DELETE にも 204 |
面接でのポイント¶
- 「どちらが正しいか」より「なぜそう設計するか」を答える: 冪等性・クライアントの要件・チームのコンベンションを根拠にする
- 冪等性(Idempotency)の概念を説明できること: 同じリクエストを複数回送っても結果が変わらない性質
- HTTP メソッドの意味論を理解していること: GET(冪等 + 安全)、PUT(冪等)、DELETE(冪等)、POST(非冪等)
- 実際のプロジェクトでの決定経験があれば話す: チームで議論して決めたことを示すと好印象