冪等性について学ぶ
今回の目的
こんにちは。API 設計について学ぶ機会があり、その中で「冪等性(べきとうせい)」というワードを知りました。今後、 API 設計をするためには、この設計概念は知っておくべきと感じ、意味について私なりに解釈することを目的に今回まとめます。
もし、間違いや指摘・意見等があればコメントよろしくお願いいたします。
冪等性の理解
まず、冪等性の意味について抑えようと思います。
冪等性とは
個人的に聞きなれない言葉ですが、冪等性とは下記のような意味です。(参考:リンク)
冪等性(idempotence)とは、ある操作を何度繰り返しても結果が同じになるという性質。元は数学の概念だが IT 分野でもほぼ同じ意味で用いられる。
私は、最初これだけ聞いても API 設計とどう絡んでいるのかよくわかりませんでした。
いろいろ調べた結果、私なりの解釈が下記になります。
「API 操作を 2 回以上実行しても、データベース側の内容が1回目の API 操作を実行した結果と変わっていない状態」
これが冪等性です。
例えばですが、GET /api/users/1
を考えてみます。
この API はユーザー ID が 1 のユーザー情報(ID と名前、年齢)を取得する API です。
1 回目実行すると下記のようなデータが返ってきました。
{
"id": 1,
"name": "Bob",
"age": 18
}
では、2度目を実行してみましょう。もちろん、同じデータが返ってきますね。
この後、何度実行しようと同じデータが返ってきます。
このように、2回以上実行しても、データベース側の内容が1回目と変わりない状態が冪等性です。また、わざわざ「データベース側の内容」と明記したのには理由があります。
なぜかというと、API のステータスコードは冪等性とは一切関係ないことを示すためです。
あくまで冪等性は、API である操作を実行した際にデータベース側の内容が1回目とそれ以降を比較した際に、変わっているか否かを確認します。
冪等性であるメリット
冪等性についての意味を抑えましたが、そもそも「冪等性を考慮し、設計する必要が本当にあるのか?」と思いませんでしょうか。冪等性を持たせる意味も分からず、作りたくはないと思い、メリットについて私なりに調べてみました。
結論から言うと、
「不整合なデータを作らせない」
これが私が冪等性で設計する一番のメリットだと思います。
冪等性があるということは、同じ操作を何度実行しても同じ結果になるということです。
つまり、下記のようなユースケースでメリットを発揮します
- 予期せぬエラー(ネットワークエラーなど)によるリクエストの再送
- ユーザーによる操作の再試行
- 分散システムにおける、複数リクエスト処理
他にもあるかと思いますが、何度実行しても結果が変わらないという特性を活かすことで、
不整合なデータを作らせず、一貫性を保証することができ、より堅牢なアプリケーションを構築することができます。
API 設計について
ここでは、冪等性を考慮し、どのように API を設計するべきか、私なりの設計方法について記載しようと思います。また今回は、下記メソッドに絞って話を進めます。
GET
DELETE
PUT
PATCH
POST
GET
GET
メソッドは冪等性を持ちます。
GET
メソッドはデータの参照を行うメソッドです。
このメソッドはデータベースに対して、変更を行わず、参照したいデータを返します。
これは、何度繰り返し実行しても同じ結果を返すため、冪等性であるといえます。
DELETE
DELETE
メソッドは冪等性を持ちます。
DELETE
メソッドはデータの削除を行うメソッドです。
DELETE
メソッドはデータベース内のデータを削除します。
例えば、ある商品を削除する API があるとします。
以下のような DELETE
のリクエストを考えます。
DELETE /api/products/1
このリクエストは ID が 1 の商品を削除するメソッドです。
このリクエストを1度送信すると、ID が 1 の商品が削除されます。
同じリクエストを送信すると、404
などのデータがないというエラーレスポンスが返ってきますが、ID が 1 のデータが削除されていることに変わりはないです。
何度実行しても、削除されたことに変わりはないためこれは、冪等性であるといえます。
PUT
PUT
メソッドは冪等性を持ちます。
PUT
メソッドはデータの一括更新または、新規作成を行うためのメソッドです。
PUT
メソッドは、同じリソースに対して同じリクエストを何度実行しても、最終的なリソースの状態は同じになります。
例えば、ある商品の価格と名前を更新する API があるとします。
以下のようなPUT
のリクエストを考えます。
PUT /api/products/1
{
"name": "apple",
"price": 200
}
このリクエストは、ID が 1 の商品の価格を 200、名前をapple
に設定します。
このリクエストを一度送信すると、商品の価格は 200 に、名前はapple
になります。
同じリクエストを再度送信しても、商品の価格は 200、名前はapple
になりますね。
つまり、何度繰り返し実行しても、最終的な商品の状態は同じになるため、PUT
メソッドは冪等性であると言えます。
PATCH
PATCH
メソッドは冪等性を持ちます。
PATCH
メソッドはリソース内の一部データを更新するためのメソッドです。
PATCH
メソッドは、同じリソースに対して同じリクエストを何度実行しても、最終的なリソースの状態は同じになります。
例えば、ある商品の価格と名前を更新する API があるとします。
以下のようなPATCH
のリクエストを考えます。
PATCH /api/products/1
{
"price": 150
}
このリクエストは、ID が 1 の商品の価格を 150 に設定します。
このリクエストを一度送信すると、商品の価格は 150 になります。
同じリクエストを再度送信しても、商品の価格は 150 になりますね。
つまり、何度繰り返し実行しても、最終的な商品の状態は同じになるため、PATCH
メソッドは冪等性であると言えます。
POST
POST
メソッドは冪等性を持ちません。
POST
メソッドは、新しいリソースを作成するメソッドです。
POST
メソッドは、同じリクエストを何度も実行すると、実行した回数だけリソースが新規作成されます。
例えば、商品を新規作成する API があるとします。
以下のようなPOST
のリクエストを考えます。
POST /api/products
{
"name": "apple",
"price": 300
}
このリクエストは、新しい商品を作成し、その名前と価格を設定します。
このリクエストを一度送信すると、新しい商品が作成されます。
しかし、同じリクエストを再度送信すると、新たに別の商品が作成され、結果として 2 つの同じ名前と価格の商品が存在することになります。
つまり、同じPOST
リクエストを何度実行しても、最終的な状態(この場合、データベース内の商品の数)は異なります。(実行した回数分、リソースが増えてしまう。)
よって、POST
は冪等性ではあるとはいえません。
最後に
今回、私なりに冪等性についてまとめてみました。設計方法については、あくまで一個人の意見です。人によっては、POST
は冪等性を持たせるべきという意見もあれば、PATCH
は冪等性を持たせるべきでないといった意見もあります。その時々のケースによって、設計方法は考えましょう。私は、現状は困っていないため、基本はこれで API 設計はしていこうと思います。
もし、何かご意見等あればよろしくお願いいたします。
Discussion