Rate Limiting
Rate limiting is a critical security feature that protects your API from abuse, DDoS attacks, and ensures fair resource usage. JifiJs provides flexible rate limiting capabilities out of the box.
π― Why Rate Limiting?β
Rate limiting protects your API from:
- Brute Force Attacks - Prevent password guessing attempts
- DDoS Attacks - Mitigate denial of service
- API Abuse - Stop scrapers and excessive usage
- Resource Exhaustion - Prevent server overload
- Cost Control - Limit expensive operations
- Fair Usage - Ensure equal access for all users
ποΈ Implementationβ
JifiJs uses express-rate-limit with Redis store for distributed rate limiting across multiple servers.
Basic Rate Limitingβ
import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';
import { redisClient } from './config/redis';
// Global rate limiter
const limiter = rateLimit({
store: new RedisStore({
client: redisClient,
prefix: 'rl:',
}),
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per windowMs
message: {
error: 'Too many requests, please try again later.',
retryAfter: 900, // seconds
},
standardHeaders: true, // Return rate limit info in RateLimit-* headers
legacyHeaders: false, // Disable X-RateLimit-* headers
});
// Apply to all routes
app.use(limiter);
Route-Specific Limitsβ
// Strict limit for authentication endpoints
const authLimiter = rateLimit({
store: new RedisStore({ client: redisClient, prefix: 'rl:auth:' }),
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 attempts
skipSuccessfulRequests: true, // Don't count successful logins
message: {
error: 'Too many login attempts. Please try again after 15 minutes.',
},
});
// Apply to specific routes
app.post('/api/auth/login', authLimiter, authController.login);
app.post('/api/auth/register', authLimiter, authController.register);
π Rate Limiting Strategiesβ
1. Fixed Windowβ
Simplest approach - fixed time windows:
const fixedWindow = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 20, // 20 requests per minute
});
Timeline:
0:00 βββββ 0:30 βββββ 1:00 βββββ 1:30 βββββ 2:00
[ββββββββ 20 req ββββββββ][ββββββββ 20 req ββββββββ]
2. Sliding Windowβ
More accurate, prevents burst at window edges:
import { rateLimit } from 'express-rate-limit';
const slidingWindow = rateLimit({
windowMs: 60 * 1000,
max: 20,
// Sliding window is default in express-rate-limit 6+
});
Timeline:
0:00 βββββ 0:30 βββββ 1:00 βββββ 1:30 βββββ 2:00
[ββββ 20 req ββββ]
[ββββ 20 req ββββ]
[ββββ 20 req ββββ]
3. Tiered Limits (User-Based)β
Different limits for different user types:
const createUserLimiter = (tier: 'free' | 'premium' | 'enterprise') => {
const limits = {
free: { windowMs: 60 * 1000, max: 20 },
premium: { windowMs: 60 * 1000, max: 100 },
enterprise: { windowMs: 60 * 1000, max: 1000 },
};
return rateLimit({
...limits[tier],
store: new RedisStore({
client: redisClient,
prefix: `rl:${tier}:`,
}),
});
};
// Middleware to apply appropriate limiter
const adaptiveLimiter = (req, res, next) => {
const user = req.user;
if (!user) {
return createUserLimiter('free')(req, res, next);
}
const limiter = createUserLimiter(user.tier);
return limiter(req, res, next);
};
app.use('/api', adaptiveLimiter);
4. Custom Key Functionβ
Rate limit by custom criteria:
// Rate limit by API key
const apiKeyLimiter = rateLimit({
windowMs: 60 * 1000,
max: 100,
keyGenerator: (req) => {
return req.headers['x-api-key'] || req.ip;
},
});
// Rate limit by user ID
const userLimiter = rateLimit({
windowMs: 60 * 1000,
max: 50,
keyGenerator: (req) => {
return req.user?.id || req.ip;
},
skip: (req) => !req.user, // Skip for unauthenticated requests
});