Idempotency in frontend when page refresh

779 Views Asked by At

Assuming that I have handled Idempotency keys and lookup/storing in backend now it is the time to handle in frontend.

I have a frontend application using reactjs and I do generate an idempotent key with each request with uuid().

I do generate the key every time the user clicks the button to Create an order. However I have some cases in mind that I think this is not the correct place to generate the key or maybe use some other mechanism that can re-use the same key in case the user refreshes the webpage. If the webpage is refreshed then a new idempotent key will be created.

So let's imagine the scenario that Order has been created but request timed out for some reason and user decides to refresh the website and try again. I am supposed to retry the same request but with same idempotent key right?

So I have thought of some possible solutions to tackle this issue:

  1. Save the idempotent key in localstorage for the request with a boolean flag isProcessed and datetime and check if request is processed, otherwise regenerate the key. However what if local storage and cache will be cleared in the meanwhile?
  2. Add an interceptor to apollo/axios in order to implement the retry mechanism with the same idempotent key, but then what happens with refresh?

Any ideas?

1

There are 1 best solutions below

0
msmer On

Using idempotency keys is the right thing to do. However, as you mentioned in your question, your frontend cannot guarantee data persistence. In React, we can use packages such as react-persist to save order data in local storage, but in some cases, this may not be reliable.

Let's imagine you have found a solution that guarantees persistent storage of order data. Even then, it does not protect you from various edge cases. For instance, a user can open two tabs in a browser, and the order data in both tabs will not be the same. Another example is when the order is created, but the frontend does not receive a response due to a network outage. Afterward, someone (e.g., a store manager) cancels the order, and the user's network starts working again. The user then clicks "order" again and receives an OK response, not realizing that the order has already been cancelled. Therefore, using idempotency keys alone is insufficient. If you stop there, you will be left with many edge cases that require processing by scripts.

A common solution that I use in my work to address the problem of state desynchronization between the frontend and backend is list versioning. Every time the user's order list changes (e.g., status, content), the server updates its version, which can be a number or a hash of the orders list stored in the database. On the frontend, with a POST request, you provide the version known to the frontend (I use If-Match header). The backend checks if the version from the frontend matches its actual version. If not, it returns a 409 response code, and you have to handle this case. You can query the user's orders list and check if the current order has already been placed. If so, you return an OK response to the user without making a new POST request for the order.

Using the backend as a single source of truth will help you eliminate many problems that arise due to state desynchronization.