Rate Limiter is a favorite LLD problem at companies that run high-traffic APIs. It tests your knowledge of multiple algorithms, distributed systems, and the Strategy pattern. Here's how to design it in an interview.
Four Algorithms You Must Know
1. Fixed Window Counter
// Count requests in a fixed time window (e.g., 100 req/min)
class FixedWindowLimiter implements RateLimitStrategy {
check(userId: string): boolean {
const windowKey = `${userId}:${Math.floor(Date.now() / 60000)}`;
const count = this.store.increment(windowKey);
if (count === 1) this.store.expire(windowKey, 60);
return count <= this.limit;
}
}Problem: Boundary burst — user can fire 200 requests at 11:59 + 12:00
2. Sliding Window Log
// Store timestamp of every request in a sorted set
class SlidingWindowLogLimiter implements RateLimitStrategy {
check(userId: string): boolean {
const now = Date.now();
this.store.removeOlderThan(userId, now - 60000);
const count = this.store.count(userId);
if (count >= this.limit) return false;
this.store.add(userId, now);
return true;
}
}3. Token Bucket (supports burst)
class TokenBucketLimiter implements RateLimitStrategy {
check(userId: string): boolean {
const bucket = this.getBucket(userId);
const now = Date.now();
const elapsed = (now - bucket.lastRefill) / 1000;
bucket.tokens = Math.min(this.capacity, bucket.tokens + elapsed * this.refillRate);
bucket.lastRefill = now;
if (bucket.tokens < 1) return false;
bucket.tokens -= 1;
return true;
}
}4. Leaky Bucket (smooth output)
Requests queue up; processed at a fixed rate. Excess requests are dropped. Use when you need smooth, predictable throughput.
Strategy Pattern for Algorithm Selection
interface RateLimitStrategy { check(userId: string): boolean; }
class RateLimiter {
constructor(private strategy: RateLimitStrategy) {}
isAllowed(userId: string): LimitResult {
const allowed = this.strategy.check(userId);
return { allowed, remaining: this.strategy.getRemaining(userId), retryAfter: allowed ? 0 : this.strategy.getRetryAfter(userId) };
}
}Layered Limits with Decorator
// Global limit + per-user limit + per-endpoint limit
const limiter = new EndpointLimiter(
new UserLimiter(
new GlobalLimiter(baseStrategy, 10000), // 10k global req/s
1000 // 1k per-user req/s
),
"/api/search",
100 // 100 search req/s
);