コンテンツにスキップ

面接問題:POST /users でユーザー作成後に返すステータスコードは200か201か

問題

POST /users should return 200 or 201 after creating user?

解答

201 Created が正しい。

解説

HTTP ステータスコードの意味

200 OK      → リクエストが成功した(汎用)
             主に GET・PUT・PATCH の成功レスポンス

201 Created → リクエストが成功し、新しいリソースが作成された
             POST でリソースを新規作成した場合に使う

RFC 9110 (HTTP Semantics) より:
  "The 201 (Created) status code indicates that the request has been
   fulfilled and has resulted in one or more new resources being created."

なぜ 201 を使うべきか

1. セマンティクスの明確化
   → API の利用者が「新規作成されたのか」「既存が更新されたのか」を
     ステータスコードだけで判別できる

2. Location ヘッダとの組み合わせ
   → 201 レスポンスには Location ヘッダで作成されたリソースの URI を含める
   HTTP/1.1 201 Created
   Location: /users/12345
   Content-Type: application/json

   { "id": "12345", "name": "Alice", ... }

3. べき等性との区別
   → 200 は「もう一度やっても同じ結果」というニュアンスがある
   → 201 は「新しいものが作られた」という事実を正確に表現

ステータスコードまとめ(REST API)

GET    /users        → 200 OK
GET    /users/:id    → 200 OK / 404 Not Found
POST   /users        → 201 Created
PUT    /users/:id    → 200 OK(更新後のリソースを返す)
PATCH  /users/:id    → 200 OK
DELETE /users/:id    → 204 No Content(ボディなし)

POST でリソース作成以外の用途(例: /login, /search):
  → 200 OK でよい(リソース作成ではないため)

実装例(Go)

func CreateUser(w http.ResponseWriter, r *http.Request) {
    var req CreateUserRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, "invalid request", http.StatusBadRequest)
        return
    }

    user, err := userService.Create(r.Context(), req)
    if err != nil {
        http.Error(w, "internal error", http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Header().Set("Location", fmt.Sprintf("/users/%s", user.ID))
    w.WriteHeader(http.StatusCreated) // 201
    json.NewEncoder(w).Encode(user)
}

面接でのポイント

  • 「201 で、Location ヘッダにリソース URI を含める」まで言えると完答
  • 「200 でも動くけど意味的に不正確」という説明ができるとベスト
  • POST が常に 201 とは限らない(/login は 200)という例外も押さえておく