Distributed systems
Rate Limiter
Cap how many requests a client can make per second, so bursts get smoothed and shared resources stay protected — without hand-written per-endpoint throttling.
✗ Problem
One client can take down the API for everyone
No cap on requests → a buggy retry loop, a scraper, or an abusive client floods an endpoint, exhausts CPU / DB connections, and every other user pays the price.
// naive endpoint — no limit at all
app.get('/api/search', (req, res) => {
runExpensiveQuery(req.query.q) // ← 10,000 req/s from one client
.then(r => res.json(r));
});
Shared resources (DB pool, CPU, downstream APIs) get exhausted — legitimate
users start seeing timeouts and 500s.
✓ How it works
→
→
Token Bucket: a budget that refills over time
A bucket holds up to N tokens and refills R tokens/sec. Each request consumes one token; an empty bucket rejects the request (HTTP 429).
class TokenBucket {
tokens = N; cap = N; rate = R;
allow() {
if (this.tokens > 0) {
this.tokens--;
return true; // 200 OK
}
return false; // 429
}
refill() { // every 1/R sec
this.tokens = Math.min(this.cap, this.tokens + 1);
}
}
Refill
R tokens/sec
Bucket
max N tokens
Request
take 1 or reject
Alternatives: fixed window (simple, but bursts at window edges),
sliding window (smoother, more bookkeeping), leaky bucket
(queues requests out at a fixed rate instead of just counting them).
✓ See it live
Drain the bucket, then refill it
Bucket cap is 5. Click Request to spend a token — empty bucket means rejected (429). Click Tick to simulate one refill interval (+1 token, capped at 5).
Tokens: 5 / 5
—
0 allowed · 0 rejected
✓ Takeaway
Cheap insurance against runaway clients
- Protects resources + fairness: one noisy client can't starve everyone else.
- Smooths bursts: short spikes are absorbed by banked tokens instead of hammering the backend.
- Key by client: per-user or per-IP buckets, not one global counter.
- Distributed limiters share bucket state in Redis (or similar) so every API instance agrees on the count.
- Respond correctly: return
429 Too Many Requestswith aRetry-Afterheader.
🎯 Combines / relates: usually a middleware step (Chain of Responsibility) and teams with the circuit breaker for resilience.
Back to all topics →