How do you safely cache retries without leaking auth or serving the wrong user?

Hey folks, I’m wiring up a frontend fetch wrapper that does retries + a tiny in-memory cache, and I’m trying to keep the security boundaries sane (no cross-user data, no stale auth). The failure mode I’m scared of is caching a 401/403 or caching a response that was fetched with one token and later served after the user switches accounts.

const cache = new Map();

export async function apiFetch(url, { token, retry = 2 } = {}) {
  const key = url; // probably too naive?
  if (cache.has(key)) return cache.get(key);

  let lastErr;
  for (let i = 0; i <= retry; i++) {
    try {
      const res = await fetch(url, {
        headers: token ? { Authorization: `Bearer ${token}` } : {}
      });

      if (!res.ok) throw new Error(`HTTP ${res.status}`);

      const data = await res.json();
      cache.set(key, data);
      return data;
    } catch (e) {
      lastErr = e;
    }
  }
  throw lastErr;
}

What’s a practical way to key/partition and invalidate this so retries and caching don’t accidentally cross auth boundaries or cache “bad” states?

1 Like