The word 'Idempotence' sounded very strange to me the first time I heard it, way back in the day, I definitely hadn't come across it in the English Language before and unfortunately, it was during an interview. So, yes, I flunked it. After the interview I went straight to Google to know what this strange word means and how it relates to programming, it turns out, I have come across it before in code, but I had no idea it was called Idempotence. You can imagine how I felt at the time.
In this article, we will be discussing Idempotence, what it is, the problem it solves and how to use it in a RESTful API
What is Idempotence?
Idempotence sounds like a 'big' word, but do not let it scare you, it's a very simple concept. Idempotence can be defined as a property of an operation such that performing it multiple times has the same effect as performing it once. In simpler terms, performing the same action repeatedly and it yields the same result.
Imagine you are building an ecommerce website, where customers select items and place orders. If a customer tries to place an order, the request gets to your server, you process the request but a response is not sent back in time, and the client app displays an error. The customer would likely retry the order, then your server creates another order, and now the customer has been charged twice. This is a bad situation you don't want to find yourself in. This can be entirely avoided if you had made your service idempotent, no matter how many times the customer tries the same request, the response is the same, so your server won't create multiple orders.
Idempotence is essential to ensure repeated operations do not lead to unintended side effects or changes in the system's state, it provides a way of maintaining consistency and reliability.
Idempotency in RESTful APIs
If you are familiar with REST, you would know that to make API requests, you'll need to use one of the following HTTP verbs GET
, HEAD
, POST
, PUT
, PATCH
, OPTIONS
, DELETE
, TRACE
, OPTIONS
and CONNECT
All of these are idempotent except for POST
and PATCH
You might also be wondering why PUT
is idempotent and PATCH
is not, this is because depending on how you implement it, PATCH
can be either idempotent or non-idempotent. You can read more about that here. This article focuses on how we can make POST
idempotent.
We know that a POST
request would end up creating a new entry in a table for each request that is sent. For Idempotency to work here we would need to be able to identify if a request has been processed before, to achieve this we introduce a unique key for each unique request (here, a unique request has not been tried before) that would let the server know whether or not it has processed this before. We call this key, the idempotency key.
In each unique request that is sent, the client generates the idempotency key and appends it to the headers of the request, the API processes the request and stores the idempotency key along with the result (response) of the operation, regardless of if it was successful or it failed. The HTTP response code is also stored. So, if an issue occurs and the client attempts to retry, it would send the same request, with the same idempotency key. Once the API sees this key, it does a simple look-up and returns the previously processed response. With that, we guarantee that the same response is returned for multiple retries of the same request. That's idempotency.
The client generating the idempotency key should consider generating random strings with enough entropy to reduce the risk of generating the same key twice (collision). The API should also implement temporary storage for these keys, and purge them later when sure they are no longer needed.
Benefits of Idempotency
Idempotent operations can be safely retried without fear of duplicate data occurring. With distributed systems, failures can occur at any time or at any point in your architecture, the use of idempotency would help improve your data's reliability and consistency.
Idempotency helps your system avoid unnecessary work! if your service has processed the request before, it just returns the original result, no need to process the request again, wasting valuable resources. This optimizes your system's performance.
Conclusion
With this understanding of idempotence, we can build robust and resilient systems that deliver predictable and consistent results.
It's advised to implement an idempotency middleware that is singularly responsible for managing idempotency. This would improve your code's maintainability.
As an added layer of security, you can compare the incoming request with the request that produced the original result to catch potential fraud or misuse.