Rate limiting is one of those things every API needs but most tutorials overcomplicate. Here are 3 practical approaches, from simplest to most robust.
Why Rate Limiting Matters
Without it, a single user (or bot) can:
- Crash your server with too many requests
- Scrape your entire database
- Run up your cloud bill overnight
Method 1: express-rate-limit (Quick & Easy)
npm install express-rate-limit
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per window
standardHeaders: true,
legacyHeaders: false,
message: { error: 'Too many requests, please try again later.' }
});
app.use('/api/', limiter);
Pros: 5 lines of code, works immediately
Cons: In-memory only — resets on server restart, doesn't work across multiple instances
Method 2: Redis-based Rate Limiting (Production Ready)
npm install rate-limit-redis ioredis
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL);
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
store: new RedisStore({
sendCommand: (...args) => redis.call(...args),
}),
});
Pros: Persists across restarts, works with multiple server instances
Cons: Requires Redis
Method 3: Sliding Window with Custom Logic
For more control, build your own:
const requestCounts = new Map();
function slidingWindowLimiter(maxRequests, windowMs) {
return (req, res, next) => {
const key = req.ip;
const now = Date.now();
if (!requestCounts.has(key)) {
requestCounts.set(key, []);
}
const timestamps = requestCounts.get(key)
.filter(t => t > now - windowMs);
if (timestamps.length >= maxRequests) {
return res.status(429).json({
error: 'Rate limit exceeded',
retryAfter: Math.ceil((timestamps[0] + windowMs - now) / 1000)
});
}
timestamps.push(now);
requestCounts.set(key, timestamps);
next();
};
}
// 50 requests per minute
app.use('/api/', slidingWindowLimiter(50, 60 * 1000));
Which Method Should You Use?
| Scenario | Recommended Method |
|---|---|
| Side project / MVP | Method 1 (express-rate-limit) |
| Production API | Method 2 (Redis) |
| Custom billing/quotas | Method 3 (Sliding window) |
Bonus: Per-User Rate Limiting
Instead of limiting by IP, limit by API key:
const limiter = rateLimit({
windowMs: 60 * 1000,
max: 30,
keyGenerator: (req) => req.headers['x-api-key'] || req.ip,
});
Key Takeaways
- Always add rate limiting — even a basic one is better than none
- Return proper
429status codes andRetry-Afterheaders - Use Redis in production for consistency across instances
- Consider different limits for different endpoints (auth vs data)
More developer guides at lucasmdevdev.github.io
Top comments (0)