Rate Limiter Design
I'm implementing rate limiting for my personal project and documenting the design before coding.
1. Why rate limiting?
To prevent clients from overloading the system and ensure stable performance on a small server.
Goals:
- Protect APIs from excessive traffic
- Prevent single-client abuse
- Safeguard expensive operations
2. What to limit?
Limit requests per client within a time window.
Example:
100 requests / minute
Exceeding the limit returns:
429 Too Many Requests
3. Algorithm choice
Options include Fixed Window, Sliding Window, and Token Bucket.
I'll use Sliding Window for the initial implementation.
4. Where to apply it?
Infrastructure layer
Client → Proxy / Load Balancer → App
- Blocks traffic early
- Limited application context
Application layer
Client → App → Rate Limiter → Controller
- Full access to user and route context
- Requests already hit the app
Combined approach
Client
↓
Infrastructure (IP-based coarse limit)
↓
Application (user/route-specific limit)
Infrastructure handles basic protection; application handles fine-grained rules.
5. Rate-limit identity
Authenticated
user_id
Unauthenticated
IP address
IP is imperfect but sufficient for initial protection.
6. State storage
Options:
- In-memory → fast but not shared across instances
- Database → too heavy for frequent updates
- Redis → fast, shared, supports TTL and atomic ops
→ Use Redis
7. Rate-limit policy
Core structure:
Identity: USER | IP
Scope: GLOBAL | ROUTE | RESOURCE
Algorithm: SLIDING_WINDOW
Store: REDIS
Limit: N
Window: T
Example:
USER + ROUTE
POST /assessments
20 requests / 60s
Controller only declares policy:
@Post()
@RateLimit(...)
createAssessment() {}
8. Rate limiting vs usage quotas
Rate limiting controls frequency, not total consumption.
Example:
Rate limit: 10 requests / hour
Usage quota: 500 generations / month
They solve different problems:
- Rate limit → request frequency
- Quota → resource consumption
Quota tracking is handled separately in the system.
9. Redis key design
Examples:
rl:user:123:route:POST:/assessments
rl:ip:192.168.1.10:route:POST:/assessments
rl:global
Final structure depends on the sliding window implementation.
Next
Implement Sliding Window using Redis, atomic operations, and Lua scripting for concurrency safety.