Rate limiting is a crucial technique for protecting APIs from abuse and ensuring fair resource usage. It controls how many requests a client can make within a specific time period. There are several approaches to rate limiting, but one of the most effective is the sliding window algorithm.
Without rate limiting, a single client could overwhelm your server with requests, causing performance issues for other users. Rate limiting helps maintain service quality, prevents abuse, and can protect against certain types of attacks like DDoS.
A fixed window approach divides time into discrete chunks (like every minute from 0-60 seconds) and resets the counter at each boundary. This can lead to "burst" problems where a client makes many requests at the end of one window and the beginning of the next.
A sliding window approach is more sophisticated. Instead of fixed time boundaries, it looks back at the last N seconds from the current moment. This provides smoother rate limiting and prevents burst issues.
The key insight is to store timestamps of requests for each client (usually identified by IP address). When a new request comes in, you:
This approach requires careful memory management since you're storing data for each client. You'll want to periodically clean up old entries to prevent memory leaks.
1class SimpleRateLimiter {
2 private requests = new Map<string, number[]>();
3
4 isAllowed(clientId: string, maxRequests: number, windowMs: number): boolean {
5 const now = Date.now();
6 const clientRequests = this.requests.get(clientId) || [];
7
8 // Remove timestamps older than the window
9 const validRequests = clientRequests.filter(timestamp =>
10 now - timestamp < windowMs
11 );
12
13 // Check if under limit
14 if (validRequests.length < maxRequests) {
15 validRequests.push(now);
16 this.requests.set(clientId, validRequests);
17 return true;
18 }
19
20 return false;
21 }
22}