🤔

冪等性について学ぶ

2024/07/01に公開

今回の目的

こんにちは。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