
REST API設計の基礎と冪等性 - HTTPメソッドの意味と Idempotency-Key
「POST を2回送ったら二重に課金された」——API設計で冪等性(idempotency)を押さえていないと起きる事故です。REST API は、HTTPメソッドの意味を正しく使うだけで、安全で予測可能になります。この記事では、リソース設計とメソッドの性質、そして冪等性を中心に、RFC 9110・MDN・Stripe を一次ソースに整理します。
RESTの基本
REST は Roy Fielding が2000年の博士論文で示したアーキテクチャスタイルで、要点はこうです。
- リソース指向: API は「操作(動詞)」ではなく「リソース(名詞)」を中心に設計する
- ステートレス: 各リクエストは自己完結(サーバーにセッション状態を持たせない)
- 統一インターフェース: HTTPメソッド + URI でリソースを操作する
「何をするか」はメソッドが担い、URL は「どのリソースか」だけを表す——これが軸です。
URL設計: 良い例と悪い例
GET /users # 一覧
POST /users # 作成
GET /users/123 # 取得
PUT /users/123 # 全体置換
PATCH /users/123 # 部分更新
DELETE /users/123 # 削除
GET /users/123/orders # ユーザーの注文一覧
# フィルタ・ソート・ページングはクエリで
GET /articles?status=published&sort=-date&limit=20&offset=0GET /getUsers
POST /deleteUser/123
POST /articles/123/update
/api/articles/1/comments/2/likes/3/reactionsURLに get / create / delete などの動詞を入れないのが基本です(それはメソッドの仕事)。
HTTPメソッドの2つの性質: safe と idempotent
RFC 9110 は、メソッドに2つの性質を定義しています。
- safe(安全): 意味として読み取り専用で、サーバー状態を変えない
- idempotent(冪等): 同じリクエストを何回送っても、サーバーへの効果が1回と同じ
| メソッド | safe | idempotent | 主な用途 |
|---|---|---|---|
GET / HEAD | YES | YES | 取得 |
OPTIONS | YES | YES | 対応メソッド確認 |
PUT | NO | YES | 完全置換 |
DELETE | NO | YES | 削除 |
POST | NO | NO | 作成・アクション |
PATCH | NO | NO(原則) | 部分更新 |
- safe なら必ず idempotent(逆は成り立たない)
PUT/DELETEは冪等。「同じ置換」「同じ削除」を繰り返しても最終状態は同じだからPOSTは非冪等。送るたびに新しいリソースができるPATCHは原則どちらでもない(差分やカウンタ加算は繰り返すと結果が変わる)
NOTE
DELETE の2回目が404でも冪等です。 冪等性は「サーバー状態の変化」の話で、返ってくるステータスコードの一致ではありません。「削除済み」という状態が変わらなければ冪等です。
なぜ冪等性が重要か
ネットワークは不確実です。レスポンスが返る前にタイムアウトしたとき、クライアントは「成功したのか失敗したのか」分かりません。安全にリトライできるかは、メソッドが冪等かどうかで決まります。
GET/PUT/DELETE: そのまま再送して安全POST: そのまま再送すると二重作成・二重課金のリスク
この「POST を安全にリトライしたい」を解決するのが、次の Idempotency-Key です。
POST を安全にする: Idempotency-Key
Stripe などが採用する仕組みで、クライアントが一意なキーを送り、サーバーが結果を記録します。同じキーの再送には最初の結果をそのまま返すため、二重実行を防げます。
POST /v1/charges HTTP/1.1
Host: api.stripe.com
Authorization: Bearer sk_live_xxxx
Idempotency-Key: a8098c1a-f86e-11da-bd1a-00112444be1e
Content-Type: application/x-www-form-urlencoded
amount=2000¤cy=jpy&source=tok_xxxx- キーは V4 UUID など一意な値(最大255文字、個人情報を含めない)
- サーバーは結果を一定期間キャッシュ(Stripe は24時間以上)。期限切れ後は新規扱い
- バリデーションエラーや並行衝突はキャッシュされず、安全に再試行できる
GET/DELETEは元から冪等なので不要
WARNING
「Idempotency-Key を付ければ POST が冪等になる」は不正確です。これはリトライを安全にする仕組みであって、メソッド自体の性質を変えるわけではありません。サーバー側の実装(キーの記録と再送時の同一レスポンス)があって初めて機能します。
ステータスコードの使い分け(要点)
| コード | 場面 |
|---|---|
200 OK | 取得・更新成功(ボディあり) |
201 Created | 作成成功。Location ヘッダで新リソースのURIを示す |
202 Accepted | 非同期処理を受理(結果は未確定) |
204 No Content | 成功・ボディ不要(DELETE や一部の PUT) |
詳しい使い分けはHTTPステータスコードの実践的な使い分けを参照してください。
エラーレスポンスは RFC 9457 で揃える
エラーの返し方は、独自JSONより標準形式(RFC 9457 / application/problem+json)に寄せると、クライアントが扱いやすくなります。
{
"type": "https://example.com/errors/out-of-credit",
"title": "残高が不足しています",
"status": 403,
"detail": "現在の残高は30ですが、この操作には50必要です",
"instance": "/account/12345/msgs/abc"
}周辺の設計トピック
- バージョニング: URLパス方式(
/v1/users)が主流で明示的。ヘッダ方式(Acceptのメディアタイプ)はURLが綺麗だが管理が複雑 - ページネーション: 小規模は
offsetで十分。大規模ではOFFSET 100000がスキャンを増やして遅くなるためカーソル方式が有利 - 認証・横断的関心事: トークンはJWT、ブラウザからの呼び出しはCORSも合わせて設計する
よくある誤解
- 「PATCH は部分更新だから安全」: 誤り。
PATCHは safe でも idempotent でもない - 「PUT と PATCH は同じ」:
PUTは完全置換(送らない項目は消えうる)、PATCHは部分更新 - 「REST = HTTP」: REST はアーキテクチャスタイル、HTTP はその実現手段(実務上はほぼ同義に使われる)
- 「DELETE は2回目も200であるべき」: 404でも冪等。状態が変わらなければよい
まとめ
- REST はリソース(名詞)中心。操作はHTTPメソッドが担う(URLに動詞を入れない)
- safe=読み取り専用、idempotent=何回でも結果が同じ。
GET/PUT/DELETEは冪等、POST/PATCHは非冪等 - 冪等性は安全なリトライの土台。
POSTは Idempotency-Key + サーバー実装で二重実行を防ぐ - 作成は
201+Location、非同期は202、本文不要は204。エラーは RFC 9457 で標準化 - バージョニング・ページネーション(カーソル)・認証も設計時に併せて考える
メソッドの意味と冪等性を押さえるだけで、API は「壊れにくく、リトライしても安全」になります。設計の8割はここで決まります。


