Retries without duplicates: derive the idempotency key from the content, not the clock
A retry is normal in any fetch; a duplicate post on a brand's feed is not. How to build an Idempotency-Key that makes the second click return what already exists, and why validation must be all-or-nothing when you publish to several channels.
Also in: Español
A retry is the most normal thing a fetch does. The connection drops halfway,
the user presses the button again, a queue redelivers a job. What is not normal
is the same post showing up twice on a brand's feed.
Two small rules in a publisher are enough to prevent both the duplicate and the half-sent batch. Here they are, in the order you hit the problems.
Rule 1: the key comes from the content
An Idempotency-Key tells the server "if you have already seen this key,
return what you created the first time instead of creating it again". Whether
that works depends entirely on where the key comes from.
- From the clock (
Date.now()): every attempt gets a different key, so the server sees two requests and creates two things. The retry is the problem the key was meant to solve. - From a random UUID generated per call: same failure. The second click generates a second UUID.
The key has to be a pure function of what is being sent. In a publisher it is built from the piece, its version and the channel, plus the card number when the upload is a single image:
const claveDe = (piezaId, version, ...resto) =>
['estudio', piezaId, `v${version}`, ...resto].join(':');
claveDe('pieza-42', 3, 'instagram'); // estudio:pieza-42:v3:instagram
claveDe('pieza-42', 3, 'card-2', 'jpeg'); // estudio:pieza-42:v3:card-2:jpeg
Same piece, same version, same channel: same key, every time. Press the button twice and the second call returns what the first one created. Edit the piece and the version changes, so the key changes and the new version goes out as the new thing it is.
Every upload and every piece carries its own key. If the network cuts out in the middle of a three-image carousel and you press again, the images that already went up are returned, not uploaded a second time.
A key is not enough: a piece already sent has to ask for it
The key protects you against a retry within an attempt. It does not protect you from someone sending the same piece next week by mistake. For that, the publisher keeps the history of what was sent, and a piece with a previous send is refused unless the call says so explicitly:
function puedeMandar(pieza, { otraVez = false, envios = [] } = {}) {
// ...only approved pieces go out...
if (envios.length && !otraVez) {
return `already sent — to send it again, otraVez: true`;
}
return null;
}
Sending again is allowed; it just cannot happen by accident.
Rule 2: validation is all-or-nothing
The other half of the problem shows up when one button publishes to several channels at once. Suppose Instagram and the email pass validation but the third variant has a caption that is too long. If you send the two valid ones and report the error on the third, the brand is now speaking on two networks and silent on the third, and nobody decided that.
So the publisher validates every variant before touching the network. If any one has problems, nothing goes out; the response lists all of them, you fix them on screen and press again:
const malas = previa.variantes.filter((v) => !v.ok);
if (previa.problemas.length || malas.length || !previa.variantes.length) {
return { ok: false, rechazo: /* every problem, with its channel */ };
}
The test for this rule was run red first: we broke a variant on purpose and checked that the call returned a rejection and made zero network calls. A check that has never failed has not been shown to work.
One boundary matters here. All-or-nothing applies to validation, before anything is sent. Once the sends start, a failure in one channel does not stop the others, and the result says which channel failed and why.
The two rules, short
- Key = f(content): piece + version + channel (+ card). Never the clock, never a random UUID. Retrying returns what already exists.
- Validate everything before sending anything: if one variant fails, none goes out.
Do you have a case where you want to see this running? Tell us what your business does and we will build it on your own material.