RESTful API設計におけるHTTPメソッドの選定:PUT、POST、PATCHの深い理解
現代のWebアプリケーション開発において、RESTful APIの設計は避けて通れない技術的基盤です。しかし、多くの開発者が直面する共通の悩みが「どのHTTPメソッドを選択すべきか」という点です。特に、リソースの更新を行う際に使用されるPOST、PUT、PATCHの使い分けは、APIの予測可能性や冪等性(Idempotency)を左右する重要な判断基準となります。本記事では、これら3つのメソッドの本質的な違いと、設計上のベストプラクティスをエンジニアの視点から詳細に解説します。
HTTPメソッドの定義と設計思想
API設計の根幹を成すのは、HTTPプロトコルの仕様(RFC 9110など)を正しく理解することです。まずはそれぞれの定義を整理します。
POSTは、リソースの「作成」を主目的とします。サーバー側がリソースのURIを決定する場合に使用されます。また、非冪等な操作(同じリクエストを繰り返すと結果が変わる可能性があるもの)として定義されます。
PUTは、リソースの「置換」を主目的とします。クライアントがリソースのURIを指定し、その場所にリクエストボディの内容を完全に上書きします。PUTは冪等であり、何度実行しても同じリソース状態になることが保証される必要があります。
PATCHは、リソースの「部分更新」を主目的とします。リソース全体ではなく、変更したいフィールドのみを送信します。PATCHは、実装によっては冪等にすることも可能ですが、仕様上は必ずしも冪等であることを要求されません。
POST:リソースの生成と万能な操作
POSTはRESTの原則において「リソースのコレクションに対して新しい要素を追加する」ために使用されます。最も重要な点は、クライアントが作成先のURIを決定するのではなく、サーバー側が生成した識別子(ID)をレスポンスとして返すという点です。
例えば、ユーザー登録を行う場合、POST /users を使用します。このリクエストを10回送れば、理論上は10人の異なるユーザーが作成されます。これが「非冪等」であるという根拠です。また、仕様の境界線が曖昧な場合に、とりあえずPOSTを選択するケース(RPC的な使い方)も現場では見受けられますが、RESTfulな設計を目指すのであれば、状態遷移を伴うアクション(メール送信や計算処理など)に限定すべきです。
PUT:完全置換による冪等性の保証
PUTは、ターゲットとなるリソースが存在しない場合は新規作成し、存在する場合は完全に置き換える「Upsert」の挙動が許容されます。PUTの最大の特徴は「冪等性」です。
PUT /users/123 を実行した際、リクエストボディに { “name”: “Alice”, “age”: 25 } を含めると、サーバー側の該当リソースは、以前どのような状態であっても、この値に強制的に書き換えられます。もしリソースに「住所」という項目が存在していても、今回のリクエストに含まれていなければ、それは削除されるか、あるいはデフォルト値に戻ることを意味します。この「全体置換」という性質が、PUTを安全かつ予測可能なものにしています。
PATCH:効率的な部分更新の実現
現代の複雑なフロントエンドアプリケーションでは、PUTの「全体置換」は非効率な場合があります。例えば、100個のフィールドを持つ大きなオブジェクトのうち、1つのフラグだけを更新したい場合に、全フィールドを送信するのはネットワーク帯域の無駄であり、また競合のリスクも高まります。
ここで登場するのがPATCHです。PATCHはリソースの差分更新を行います。ただし、JSON形式で単にフィールドを送るだけでは、配列の扱い(追加なのか置換なのか)が曖昧になることがあります。そのため、実務では「JSON Patch (RFC 6902)」のような形式を採用し、どのような操作(add, remove, replaceなど)を行うかを明示することが推奨されます。
サンプルコード:RESTfulな更新処理の実装例
以下に、Node.js (Express) を想定した、PUTとPATCHの実装の違いを示す概念コードを提示します。
// PUT: 完全置換の例
app.put('/api/users/:id', (req, res) => {
const userId = req.params.id;
const newUserData = req.body;
// データベース上の既存データを完全に置き換える
// 含まれていないフィールドは null またはデフォルト値になる
db.users.replaceOne({ id: userId }, newUserData);
res.status(200).json({ message: 'Resource fully replaced' });
});
// PATCH: 部分更新の例
app.patch('/api/users/:id', (req, res) => {
const userId = req.params.id;
const updates = req.body;
// 指定されたフィールドのみを更新する
// $set は MongoDB 等でよく使われる部分更新の演算子
db.users.updateOne({ id: userId }, { $set: updates });
res.status(200).json({ message: 'Resource partially updated' });
});
実務アドバイス:設計の現場で迷わないために
実務においてこれらのメソッドを使い分ける際、以下の判断基準を参考にしてください。
1. 冪等性が必要か:何度繰り返しても問題がない処理(設定の保存など)であればPUTを検討してください。逆に、操作回数が意味を持つ(ポイント加算など)場合はPOSTを選択します。
2. データモデルの複雑さ:フィールドが少ないシンプルなモデルであればPUTでも問題ありません。しかし、データ構造が大きく、クライアント側で変更箇所を特定できる場合はPATCHを導入することで、APIのレスポンス速度と保守性が向上します。
3. セキュリティと競合:PATCHを使用する場合、複数のクライアントが同時に同じリソースを更新すると、意図しないデータの上書きが発生する可能性があります。これを防ぐために、ETag(Entity Tag)を用いた「楽観的ロック」を併用することが不可欠です。If-Matchヘッダーを使用して、更新対象のリソースが変更されていないことを確認してから処理を行うのが、プロフェッショナルな設計です。
4. フレームワークの制約:一部の古いプロキシやファイアウォールでは、PUTやPATCHをブロックする設定になっている場合があります。しかし、現代のクラウドネイティブな環境ではこの制約はほぼ存在しません。仕様に忠実な設計を選択してください。
まとめ:RESTful APIの品格を高めるために
POST、PUT、PATCHの使い分けは、単なる規約の遵守ではありません。それはAPIを利用するクライアント側に対して「この操作は安全なのか」「この操作はリソースをどう変えるのか」という明確なコミュニケーションを行う行為です。
– POSTは「新規作成と非冪等なアクション」
– PUTは「リソースの完全置換と冪等な更新」
– PATCHは「リソースの効率的な部分更新」
これらを正しく理解し、適切に使い分けることで、APIの可読性と堅牢性は飛躍的に向上します。設計段階でこれらの方針をチーム内で共有し、ドキュメントに明記しておくことが、長期的なメンテナンスコストを削減する鍵となります。技術的な正しさを追求することは、開発者体験(DX)を向上させ、結果としてビジネスのスピードを加速させることに直結するのです。

コメント