Advanced API Rate Limiting Strategies in Node.js With Redis and Express
Advanced API Rate Limiting Strategies in Node.js With Redis and Express Rate limiting is essential for protecting your API from abuse and ensuring fair usage among users. While basic in-memory counters might work for small applications, scalable systems require more robust strategies. In this post, we'll implement advanced rate limiting in Express.js using Redis, enabling us to track requests per user across distributed systems and customize limits per endpoint or user tier. Why Use Redis for Rate Limiting? Persistence across servers: Redis stores rate limit data centrally, not per instance. Speed: Redis is optimized for fast reads and writes. TTL support: Easily expire counters after the window resets. Step 1: Set Up Express and Redis npm install express ioredis // server.js const express = require('express'); const Redis = require('ioredis'); const app = express(); const redis = new Redis(); // defaults to localhost:6379 const WINDOW_SIZE_IN_SECONDS = 60; const MAX_REQUESTS = 10; Step 2: Middleware for Global Rate Limiting async function rateLimiter(req, res, next) { const ip = req.ip; const key = `rate-limit:${ip}`; const current = await redis.incr(key); if (current === 1) { await redis.expire(key, WINDOW_SIZE_IN_SECONDS); } if (current > MAX_REQUESTS) { const ttl = await redis.ttl(key); return res.status(429).json({ message: "Too many requests. Try again in " + ttl + " seconds.", }); } next(); } Step 3: Customize by User Tier Suppose you have users with roles: "free", "pro", and "enterprise". You can scale the logic: const limits = { free: 30, pro: 100, enterprise: 1000, }; async function tieredLimiter(req, res, next) { const user = req.user || { id: req.ip, role: "free" }; // Mock user const key = `rate-limit:${user.id}`; const max = limits[user.role] || 30; const current = await redis.incr(key); if (current === 1) { await redis.expire(key, WINDOW_SIZE_IN_SECONDS); } if (current > max) { const ttl = await redis.ttl(key); return res.status(429).json({ message: `Rate limit exceeded (${user.role}). Try again in ${ttl}s.`, }); } next(); } Step 4: Route-Level Controls Apply different middleware functions for public APIs, auth, and sensitive routes: app.use('/api/public', rateLimiter); app.use('/api/user', tieredLimiter); app.get('/api/public/data', (req, res) => { res.send({ message: "Public data" }); }); app.get('/api/user/data', (req, res) => { res.send({ message: "User-specific data" }); }); Step 5: Bonus - Sliding Window Logs (Optional) For more fairness, use Redis sorted sets to implement a sliding log algorithm. Store timestamps for each request and count them in the last N seconds. This provides a smoother limit than fixed windows but is more complex. Conclusion Implementing rate limiting with Redis in Node.js allows your API to remain responsive and protected under heavy load. Whether you're building a SaaS app or a public API, adapting rate limits by route and user tier gives you control and flexibility. If this post helped you, consider supporting me: buymeacoffee.com/hexshift
Advanced API Rate Limiting Strategies in Node.js With Redis and Express
Rate limiting is essential for protecting your API from abuse and ensuring fair usage among users. While basic in-memory counters might work for small applications, scalable systems require more robust strategies. In this post, we'll implement advanced rate limiting in Express.js using Redis, enabling us to track requests per user across distributed systems and customize limits per endpoint or user tier.
Why Use Redis for Rate Limiting?
- Persistence across servers: Redis stores rate limit data centrally, not per instance.
- Speed: Redis is optimized for fast reads and writes.
- TTL support: Easily expire counters after the window resets.
Step 1: Set Up Express and Redis
npm install express ioredis
// server.js
const express = require('express');
const Redis = require('ioredis');
const app = express();
const redis = new Redis(); // defaults to localhost:6379
const WINDOW_SIZE_IN_SECONDS = 60;
const MAX_REQUESTS = 10;
Step 2: Middleware for Global Rate Limiting
async function rateLimiter(req, res, next) {
const ip = req.ip;
const key = `rate-limit:${ip}`;
const current = await redis.incr(key);
if (current === 1) {
await redis.expire(key, WINDOW_SIZE_IN_SECONDS);
}
if (current > MAX_REQUESTS) {
const ttl = await redis.ttl(key);
return res.status(429).json({
message: "Too many requests. Try again in " + ttl + " seconds.",
});
}
next();
}
Step 3: Customize by User Tier
Suppose you have users with roles: "free", "pro", and "enterprise". You can scale the logic:
const limits = {
free: 30,
pro: 100,
enterprise: 1000,
};
async function tieredLimiter(req, res, next) {
const user = req.user || { id: req.ip, role: "free" }; // Mock user
const key = `rate-limit:${user.id}`;
const max = limits[user.role] || 30;
const current = await redis.incr(key);
if (current === 1) {
await redis.expire(key, WINDOW_SIZE_IN_SECONDS);
}
if (current > max) {
const ttl = await redis.ttl(key);
return res.status(429).json({
message: `Rate limit exceeded (${user.role}). Try again in ${ttl}s.`,
});
}
next();
}
Step 4: Route-Level Controls
Apply different middleware functions for public APIs, auth, and sensitive routes:
app.use('/api/public', rateLimiter);
app.use('/api/user', tieredLimiter);
app.get('/api/public/data', (req, res) => {
res.send({ message: "Public data" });
});
app.get('/api/user/data', (req, res) => {
res.send({ message: "User-specific data" });
});
Step 5: Bonus - Sliding Window Logs (Optional)
For more fairness, use Redis sorted sets to implement a sliding log algorithm. Store timestamps for each request and count them in the last N seconds. This provides a smoother limit than fixed windows but is more complex.
Conclusion
Implementing rate limiting with Redis in Node.js allows your API to remain responsive and protected under heavy load. Whether you're building a SaaS app or a public API, adapting rate limits by route and user tier gives you control and flexibility.
If this post helped you, consider supporting me: buymeacoffee.com/hexshift