⛓️

POST の API には冪等性を持たせるべき

2023/09/01に公開

はじめに

MDN に記載されているように、POST は冪等性を持たせることが考慮された HTTP メソッドではありません。
以下は MDN からの引用です。

PUT と POST との違いは、 PUT がべき等であることです。一度呼び出しても複数回呼び出しても成功すれば同じ効果になる(副作用がない)のに対して、同じ POST に成功すると、複数回の注文を行うような、追加の効果が出ます。

しかし、私はそれでも POST の API に冪等性を持たせるべきだと考えています。

冪等性とは

冪等性とは、同じ操作を何度繰り返しても、結果が変わらずに一貫して同じ状態になる性質のことを指します。

例えば、窓を拭くことを考えてみましょう。
窓を100回拭いたとしても、最終的には同じ綺麗な窓になります。
つまり、拭く操作は冪等性を持っていると言えます。

一方で、窓を割るという操作は冪等性を持ちません。
窓を1回割ると窓の状態が変化し、2回目以降も同じ窓を割ってしまうと、窓はさらに壊れてしまうため、結果が同じ状態にはならないからです。

冪等性を持たせるべき理由

API に冪等性を持たせることで、エラーが起きても簡単にリトライすることが可能となります

例えば、ユーザーが支払いを行うケースを考えてみます。
支払いの API は冪等性を持っておらず、実行するたびに口座からお金が引き落とされる仕組みです。
クライアントがこの API を呼び出した際に、通信の問題やタイムアウトなどが原因でレスポンスを受け取れない場合、クライアント側は支払いの状況を把握できなくなってしまいます。

これは支払い処理において重大な問題ですが、この API に冪等性を持たせることで、この問題を解決することが可能です。
クライアントは処理が成功するまでリトライを続け、サーバー側は同じリクエストを複数回受け取った場合、内部の支払いなどは行わずに最初のレスポンスを返すことで解決します。

つまり、支払いが重複して行われることを防ぎつつ、クライアント側は安定した結果を取得することができるのです。

実装方法

API に冪等性を持たせるため、リクエストヘッダに Idempotency-Key (冪等キー)を設定し、サーバー側でこれを管理することで実装できます。
Idempotency-Key の形式は UUID がオススメです。

サーバー側の実装では、冪等キーを受け取った場合、まず DB に同じの冪等キーが存在するかどうかを確認します。
もし既に存在するかつ、冪等キーの有効期限が切れていない場合は、内部的な処理を行わずに既存のデータを元にレスポンスを生成します。
有効期限内の同一の冪等キーを使い続ける限りは、何度リクエストを受信しても同じ結果を返します。

別の方法として、リソースの ID を活用して API に冪等を持たせることもできます。
例えば /items/{:id}id を冪等キーとしてリクエストを管理するイメージです。
ただし、利用者に対して API が冪等性を持つことを明示的に伝えたいため、冪等キーを使った実装をオススメします。

おわりに

外部の API の仕様書を読んでみると、多くの API が冪等性を持っていることに気付きました。
冪等性を持った API は、何か問題が起こった時にリトライがしやすく、いち利用者としては非常に助かっています。
このような経験から、冪等性に関する情報を皆さんに伝えるため、この記事を執筆しました。

皆さんのお役に立てれば幸いです。

Discussion