TIL: Implementing Exponential Backoff in TypeScript
APIs fail. Networks glitch. The right response is to retry with increasing delays.
async function fetchWithRetry(url: string, maxRetries = 3): Promise<Response> {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url)
if (response.ok) return response
const delay = Math.min(1000 * Math.pow(2, i) + Math.random() * 1000, 10000)
console.log(`Retry ${i + 1} after ${delay}ms`)
await new Promise(r => setTimeout(r, delay))
}
throw new Error('Max retries exceeded')
}
Delay doubles each attempt: 1s, 2s, 4s. Random jitter prevents thundering herd when multiple clients retry simultaneously. The cap at 10s stops the delay growing unbounded.
const result = await fetchWithRetry('https://api.example.com/v1/listen')
The function looks like a normal call. The caller does not need to know about retry logic.
What status codes should I retry on?
429 (rate limited) and 5xx (server errors). Do not retry 4xx errors like 400 or 401.
Should I log retries?
Yes. Log each attempt with the attempt number and delay. Debugging flaky integrations is much easier with that trace.