JavaScriptを有効にしてください

RESTfulAPIにおける冪等性の担保

 ·   1 分で読めます

はじめに

冪等性についてご存知でしょうか。
最近RESTfulAPIの設計を行うことがあり、色々と調べて知見を得たので記事にまとめました。
この記事では冪等性についての説明から、RESTfulAPIでの具体的な冪等性の設計手法を説明します。
読んだ方のAPI設計の悩みが1つでも解消できれば幸いです。

想定読者

この記事はRESTfulAPIの設計や実装に関わっているが冪等性について詳しくない人を対象にしています。
この記事を呼んで “冪等性を完全に理解した(※1)” 状態になることを目指して書きました。

冪等性とは

冪等性とは同じ操作を何回繰り返しても同じ結果が得られるということを言います。
例えばテレビのリモコンの"1"ボタンは冪等性があると言えます。
“1"を押すと1チャンネルに切り替わりますが、その時点でもう一度"1"ボタンを押しても1チャンネルのままです。つまり、“1"ボタンを何度押しても"1チャンネルを表示する"という結果は変わらないので冪等と言えます。
逆に冪等性が無い状態というのは、同じ操作を繰り返した際に結果が異なる場合を言います。
例えばテレビのリモコンの電源ボタンは冪等性がありません。
テレビがついていない状態で電源ボタンを押すとテレビが付きますが、もう一度押すと今度は消えてしまいます。同じ操作を繰り返し行った際に結果が異なるので冪等とは言えません。

RESTfulAPIにおける冪等性

RESTfulAPIにおける冪等性は、何度APIを呼び出しても結果が常に同じ(リソースの状態が変わらない)ことを言います。
Idempontent REST APIsによると、REST原則に従って設計すれば、HTTPメソッドよって冪等性があるか、そうでないかを分類することができます。

  • POST は冪等ではありません。
  • GET, PUT, DELETE, HEAD, OPTIONS, TRACE は冪等です。

POSTは通常新しいリソースを作成するために使用します。
POSTリクエストを同じ内容で10回呼び出せば、10個のリソースが作成されることになります。よって、POSTには冪等性がありません。
GET, HEAD, OPTIONS, TRACE はリソースを取得する処理なので、リソースの更新を一切行わないことから冪等といえます。
PUT, DELETEはリソースの更新を行いますが、何度同じリクエストを送っても更新結果は変わりません。なので、こちらも冪等といえます。

この記事にはPATCHについて言及されていませんが、APIの実装により冪等である場合もあるし、冪等でない場合もあることだと私は認識しています。
いずれせよ、PATCHの場合もPOSTと同じように後述する冪等性を担保する設計を行えば冪等性を担保することができます。

なぜ冪等性が必要なのか

インターネットを経由した通信では、しばしば途中で通信エラーが発生してレスポンスが受け取れないケースが発生することがあります。
レスポンスが受け取れない場合、送ったリクエストが正しく処理されたか判断できないため、リトライ処理を行う必要があります。
冪等性の無いAPI(例えばPOSTメソッド)の場合、何も考えずにリクエストを送り直すとリソースが重複して登録されてしまう可能性があります。
その場合、サーバー側のリソースの状況を取得するAPIを呼び出すなど再度リクエストを送って問題ないかチェックしてからリトライを行う必要があり、通信エラー発生時のリトライ処理が煩雑になってしまいます。
冪等性のあるAPIの場合は改めてリクエストを送り直すだけでこの問題を解決することができます。
また、通信エラー以外にも一時的なエラーの場合は同様にリトライ処理で解決することができます。

冪等性のあるAPIを設計する

冪等性が無いとされているPOSTメソッドで冪等性を担保するための方法を説明します。
冪等性を担保するために呼び出し元から “冪等性キー” をリクエストに渡します。
APIを提供するサーバー側では “冪等性キー” を受け取り、冪等性キーの値が既に処理済みであるかチェックを行います。
これまで処理したことの無い “冪等性キー” であれば、リソース作成処理を行い、処理済みの “冪等性キー” としてストレージに登録します。
処理済みの “冪等性キー” であれば、リソース作成処理を行わずに終了します。
このような実装にすることで、同じ “冪等性キー” で何度呼び出しても1つのリソースしか作成されなくなり、冪等であると言える状態を作ることができます。

具体的な設計方針

2022年4月現在、決定的な推奨方法は無いようですが、ここでは “Idempotency-Key Header” を利用する方法を紹介します。
これは上記で説明した “冪等性キー” を HTTP Header に渡す方法です。
呼び出し元のクライアントで UUID を採番し、それを HTTP Header の “Idempotency-Key” に渡すことで冪等性を担保します。

  • ユーザー登録APIの例
    • URL: https://{your_domain}/users
    • Method: POST
    • Header: Idempotency-Key={採番したUUID}
    • Body: “{user_id:12345, user_name:Tom}”

この方法は2021年11月にIETFに提案されたようですが、採用されることはなく2022年1月に有効期限を迎えてしまいました。
Idempotency-Key Headerの現状・仕様・実装の理解を助けるリソースまとめを参考にさせていただきました。)
HTTP Headerに指定する方法以外にもリクエストボディに同様の冪等性キーを指定することで、冪等性を担保することもできます。
推奨される方法はないため、プロダクトに合った方法で実装すれば良いと思います。

まとめ

冪等性のあるRESTfulAPIについて紹介しました。
POSTメソッドでの冪等性の担保はそれほど高いコストをかけること無く実装することができると思います。
呼び出すクライアント側のリトライ処理が格段に楽になりますので、冪等性の担保について検討してみてはいかがでしょうか。
読んだ方のAPI設計の悩みが1つでも解消できれば幸いです。

参考記事

冪等と安全に関する誤解
Idempontent REST APIs
Idempotency-Key Headerの現状・仕様・実装の理解を助けるリソースまとめ

注釈

※1 : 完全に理解した → 製品を利用するためのチュートリアルを完了できたという意味。

共有

ktzw
著者
ktzw
東京でWebエンジニアをやっています。サーバーサイドとクラウドインフラいじりが好きです。最近はマネジメントもやってます。