Skip to catalogue

request deduplication

Share equivalent requests within a defined scope.

Use this when Several components request the same data at the same time.

without the term

“In the network tab I see the same API call fired four times on page load because several components each fetch the user. Can you make them share one request?”

with the term · How do you ask a model for request deduplication?

Deduplicate these equivalent requests through the existing query cache. Verify one network call and preserve user-specific cache keys.

XBlueskyLinkedIn

What is request deduplication?

Request deduplication lets callers reuse an in-flight operation or cached result identified by the same key.

Why does request deduplication matter when vibe coding?

Repeated fetch code can quietly multiply traffic.

How do you do request deduplication?

Include all result-changing inputs in the key. Check one request for equivalent callers and separate results for different users.

What goes wrong with request deduplication?

An overly broad key shares data between different users or filters.

Before and after

Two components request the same profile at the same time. We want one underlying read while it is pending, but different users must never share a response and a failed read must be retryable.

Before

Each caller invokes the loader independently. Equivalent concurrent reads start twice, even though both callers need the same result.

function before(load) {
  return (user, resource) => load(user, resource);
}

After

Keep a promise for each equivalent read within one owner. Include user identity and resource identity in the key. Return the existing pending promise, then remove it on either success or failure so later calls can refresh.

function after(load) {
  const pending = new Map();
  return (user, resource) => {
    const key = JSON.stringify([user, resource]);
    if (!pending.has(key)) {
      const request = Promise.resolve()
        .then(() => load(user, resource))
        .finally(() => pending.delete(key));
      pending.set(key, request);
    }
    return pending.get(key);
  };
}

Check the change

Two equivalent concurrent calls produce two loader calls before and one shared call afterward. Different users remain separate. Settled reads are removed, and a shared rejected read can be tried again.

Save the runnable comparison, then run node --test request-deduplication.mjs with Node.js 22.12 or later.

Download the complete teaching example: the bug, prompt, runnable code, expected result, limits, and sources, linked back to this page.

Show the assertions
test('equivalent callers start two reads without deduplication', async () => {
  let calls = 0;
  const read = before(async () => { calls++; return 'profile'; });
  await Promise.all([read('alice', 'profile'), read('alice', 'profile')]);
  assert.equal(calls, 2);
});

test('reads share only within scope and failures do not stick', async () => {
  let calls = 0;
  const read = after(async (user) => { calls++; return user; });
  const a = read('alice', 'profile');
  const b = read('alice', 'profile');
  assert.equal(a, b);
  assert.deepEqual(await Promise.all([a, b, read('bob', 'profile')]),
    ['alice', 'alice', 'bob']);
  assert.equal(calls, 2);
  await read('alice', 'profile');
  assert.equal(calls, 3);
  let attempts = 0;
  const retry = after(() => {
    if (++attempts === 1) throw new Error('offline');
    return 'recovered';
  });
  const failed = await Promise.allSettled([retry('alice', 'profile'), retry('alice', 'profile')]);
  assert.deepEqual(failed.map((item) => item.status), ['rejected', 'rejected']);
  assert.equal(attempts, 1);
  assert.equal(await retry('alice', 'profile'), 'recovered');
  assert.equal(attempts, 2);
});

When this is not enough

This in-memory example shares pending reads in one owner only. It is not a persistent cache, a distributed lock, or an idempotency guarantee for writes. Include every result-changing input in the key; create the owner inside the correct account or request lifetime. Decide how cancellation works when several consumers share one promise. Prefer an existing query cache when it already provides the needed lifecycle.

The next decision

Sources and verification

Sources checked . These are illustrative examples with automated checks. How we check content.

Did this help with your task?

After trying the prompt or check, tell us what happened. This optional response records only your choice and this term.

Choose between related terms

adjacent

Expanded example updated .

Historical short-entry review

report a correction