Building Resilient APIs: Rate Limiting, Retries, and Circuit Breakers
APIs fail. Networks are unreliable. Learn three essential patterns—rate limiting, exponential backoff with retries, and circuit breakers—that make your services resilient under pressure.
# Building Resilient APIs: Rate Limiting, Retries, and Circuit Breakers Distributed systems fail in partial, unpredictable ways. A downstream service gets slow; a third-party API returns 429s; a network partition drops packets. These three patterns help your system survive gracefully. ## 1. Rate Limiting Protect your API from being overwhelmed by too many requests—whether from one bad actor or a thundering herd. **Token Bucket algorithm** (most flexible): ```typescript // Using a Redis-backed token bucket const allowed = await rateLimiter.consume(userId, 1); if (!allowed) { res.status(429).json({ error: "Too many requests" }); return; } ``` Return the `Retry-After` header so clients know when to retry. ## 2. Retries with Exponential Backoff Don't retry immediately—you'll amplify the load on an already struggling service. Use exponential backoff with jitter: ```typescript async function fetchWithRetry(url: string, maxRetries = 3): Promise<Response> { for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await fetch(url); } catch (err) { if (attempt === maxRetries) throw err; const delay = Math.min(1000 * 2 ** attempt + Math.random() * 100, 30000); await new Promise(r => setTimeout(r, delay)); } } } ``` Only retry on transient errors (5xx, network timeouts)—not 4xx. ## 3. Circuit Breakers A circuit breaker tracks failure rates and stops sending requests when a service is clearly down. **States:** - **Closed** – Normal operation; requests flow through. - **Open** – Too many failures; requests fail immediately without hitting the service. - **Half-Open** – Test a single request; if it succeeds, close the circuit. Libraries: `opossum` (Node.js), `resilience4j` (Java), `Polly` (.NET). ## Putting It Together ``` Incoming Request │ ┌────▼────┐ │ Rate │ → 429 if exceeded │ Limiter │ └────┬────┘ │ ┌────▼────┐ │ Circuit │ → Fail fast if Open │ Breaker │ └────┬────┘ │ ┌────▼────┐ │ Retry │ → Backoff on failure │ Logic │ └────┬────┘ │ Downstream Service ``` These three patterns together make the difference between a system that cascades into failure and one that degrades gracefully.
