How do you keep a retrying fetch from poisoning your cache with errors?

Yo Kirupa folks, I’m wiring up a little API client in a React app and I’m trying to add retries plus a tiny in-memory cache so I don’t spam the server. The failure mode I’m hitting is nasty: if the first request fails (503/offline), I end up caching the rejected promise and every later call instantly fails until a full refresh.

const cache = new Map();

export function getUser(id) {
  const key = `user:${id}`;
  if (cache.has(key)) return cache.get(key);

  const p = fetch(`/api/users/${id}`)
    .then(r => {
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
      return r.json();
    })
    .catch(async (err) => {
      await new Promise(r => setTimeout(r, 300));
      return fetch(`/api/users/${id}`).then(r => r.json());
    });

  cache.set(key, p);
  return p;
}

What’s the cleanest pattern to avoid caching failures while still deduping in-flight requests (and not accidentally triggering a thundering herd)?

1 Like