In this article, we’ll see what an idempotent operation is, why idempotency is useful, and we’ll look at idempotency in REST.
2. What is Idempotence?
Simply put, we can perform an idempotent operation multiple times without changing the result.
Furthermore, the operation must not cause any side effects after the first successful execution.
Let’s look at two simple examples.
2.1. Absolute Value
A function that returns the absolute value is idempotent; no matter how often we apply it to the same number, it always returns the same result.
Let’s consider the function:
Then the following is true:
In contrast, a function that flips the sign of a number is not idempotent:
2.2. Update Address
Another practical example of an idempotent operation would be to update the contact details of a user in the system. Let’s say we update only the telephone number. The side effect of this update is that we send a text message with a verification code to the new number. We have three possible scenarios:
- The first update message was processed successfully, the number was updated in the system, and the text message was sent. When the update message is sent again, nothing will happen. Also, the text message isn’t sent for a second time.
- The first update fails. The system isn’t updated, and no text message is sent. If the second update is successful, the system is updated, and the text message is sent.
- The first update fails. Before another update is sent, the user is deactivated in the system. Because the system state has changed, a second update of the telephone number doesn’t affect.
3. Why Idempotence?
Idempotence ensures that the same request leads to the same system state, and no action is unintentionally executed more than once.
As an example, let’s look at a request from sender S to send money via a payment service PS to the receiver R.
3.1. Non-Idempotent Example
Here’s the non-idempotent version of the request:
In the first try, S sends a request to send $10 to R. PS receives the messages; however, the actual transfer fails. PS sends returns an error message to S who doesn’t receive that message due to a network failure.
S doesn’t know if the transfer was successful, so he tries again. This time the transfer to R is successful, and PS sends a confirmation message to S. Again, the confirmation fails, and S doesn’t know if the transfer was successful or not.
Therefore, he tries for the third time. PS receives the message, regards it as a new request, sends the money to R, and returns a confirmation to S.
This isn’t an idempotent request because we intended to retry the same payment and not send it twice.
3.2. Idempotence Key
Payments are a good example to illustrate why idempotence is useful. In the previous example, we’ve seen that the payment to R is executed multiple times because S retires without knowing that the transfer already had been successful.
If the operation was idempotent, this wouldn’t have been the case. But how does PS know that S just has retried the same payment and doesn’t want to send a second payment of $10 to S?
To achieve this, S includes an idempotence key in his request to PS. This key can be, for example, is a UUID. If PS receives a request with the same idempotence key, it knows that it’s a retry. If it hasn’t seen the key before, it knows that it’s a new request.
Let’s look at the idempotent version of the previous example:
Here, the first try and first retry are the same as in the non-idempotent version, except that the request includes the idempotence key (65TH68M5 in our example).
The important difference is the second retry: PS receives the message, finds that a message with the same idempotence key was already processed successfully, and returns a confirmation to S without sending the money to R again.
Here, the benefit of idempotency is that S can retry as many times as he wants without having to worry about a double payment. PS doesn’t need to worry that S receives the confirmation, as he knows that S can retry in case he hasn’t received the message.
The disadvantage of this approach is that PS needs to store all received keys. This might be fine if there aren’t many requests; however, if the request frequency is very high, it might be problematic. A solution can be to expire the idempotence key after some time.
4. Idempotence and REST
Let’s have a brief look at how idempotency applies to HTTP verbs, which are important to understand when we want to build a REST API. A very detailed reference of all HTTP verbs can be found in RFC 7231.
The following table shows which verbs are (or should be) idempotent.
4.2. Idempotent Operations
GET, HEAD, and OPTION are clearly idempotent as they only read data, but don’t create, update or delete any resources.
PUT is idempotent as it updates a resource or creates a new one if it doesn’t exist. If we sent the same update multiple times, the resource shouldn’t change.
4.3. Non-Idempotent Operations
POST doesn’t have to be idempotent as it creates a new resource and, if called again, usually creates another resource. However, it can be implemented as an idempotent operation as well.
The PATCH operation updates a resource partially and doesn’t necessarily have to be idempotent. Let’s look at an example to understand the difference between PUT and PATCH better:
Let’s say we want to add an item to a shopping cart in an online shop. If we use PUT, we need to send the complete shopping cart data, including all items which are already in the cart. With PATCH, we can send only the item to be added, and it will be appended to the list of items already in the cart.
If we send the PATCH request again, the same item will be added again. Of course, as for POST, we can implement an idempotent PATCH as well.
4.4. HTTP Response
It’s important to note that several calls to an idempotent operation do not necessarily result in the same HTTP response.
PUT, for example, will return 201 (Created) if a resource is created, or 200 (OK) or 203 (No Content) if a resource was updated.
A DELETE, for example, can returns 200 (OK) or 204 (No Content) when an actual deletion takes place. Any subsequent calls will return 404 (Not found).
In this article, we saw what idempotence means, what are the benefits of idempotence, and how it relates to REST.
Even though the general meaning of idempotence is easy to understand, it can be quite tricky to consider all the subtleties like side effects and HTTP responses during the design of an API.