Retry-After Header Backoff

Interpret a Retry-After header as seconds or an HTTP date and cap the delay before the next retry attempt.

JavascriptResilience

Code

snippet
export function retryDelay(header, { now = Date.now(), maxDelayMs = 60000, fallbackMs = 1000 } = {}) {
  if (header === undefined || header === null || String(header).trim() === '') return fallbackMs;
  const raw = String(header).trim();
  if (/^\d+$/.test(raw)) {
    return Math.min(Number(raw) * 1000, maxDelayMs);
  }
  const when = Date.parse(raw);
  if (Number.isNaN(when)) return fallbackMs;
  return Math.min(Math.max(when - now, 0), maxDelayMs);
}

export async function fetchWithRetry(url, options = {}, maxAttempts = 3) {
  let lastError;
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    const res = await fetch(url, options);
    if (res.status !== 429 && res.status < 500) return res;
    if (attempt === maxAttempts) return res;
    lastError = res;
    const delay = retryDelay(res.headers.get('retry-after'));
    await new Promise((resolve) => setTimeout(resolve, delay));
  }
  throw lastError;
}