When I was building my backend API, I realized a big problem: anyone could spam my endpoints.
If a user repeatedly reloads a page or hits an endpoint calling an external AI service, it can crash the server or run up high API costs.
To fix this, I added Rate Limiting. Here is why I used Redis for it and how I set it up using ES Modules.
The Problem with Simple In-Memory Limiters
At first, I thought about saving request counts in a simple JavaScript object:
// ❌ Simple in-memory check (Not good for production)
const requestCounts = {};
app.use((req, res, next) => {
const ip = req.ip;
requestCounts[ip] = (requestCounts[ip] || 0) + 1;
if (requestCounts[ip] > 100) {
return res.status(429).json({ error: "Too many requests" });
}
next();
});
This works locally, but has two big flaws:
1.Memory Leaks: The requestCounts object keeps growing in memory forever.
2.Breaks when Scaling: If you deploy multiple instances of your app behind a load balancer, each server keeps its own count. A user can easily bypass the limit by hitting different servers.
The Solution: Centralized Redis Store
Redis stores data in RAM outside our Node.js app. Because it is centralized, all server instances share the exact same count.
[ Incoming Requests ]
│
[ Cloud Load Balancer ]
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
[ Express Node 1 ] [ Express Node 2 ] [ Express Node3]
│ │ │
└───────────────────┼───────────────────┘
▼
[ Central Redis Store ]
How I Configured It in My Project
In my app, I use two levels of protection:
-Global Limit: 100 requests per 15 minutes for normal routes.
-Strict Limit: 5 requests per 10 minutes for heavy routes (like AI generation or OTP emails).
- Redis Connection (config/redis.js)
JavaScript
import { createClient } from 'redis';
const redisClient = createClient({
url: process.env.REDIS_URL || 'redis://localhost:6379'
});
redisClient.on('error', (err) => console.error('Redis Error:', err));
redisClient.on('connect', () => console.log('Connected to Redis'));
await redisClient.connect();
export default redisClient;
- Middleware Setup (middlewares/rateLimiter.js)
JavaScript
import { rateLimit } from 'express-rate-limit';
import { RedisStore } from 'rate-limit-redis';
import redisClient from '../config/redis.js';
// Global Limiter (100 req / 15 mins)
export const globalRateLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
limit: 100,
standardHeaders: 'draft-7',
legacyHeaders: false,
store: new RedisStore({
sendCommand: (...args) => redisClient.sendCommand(args),
}),
message: {
success: false,
error: 'Too many requests. Please try again in 15 minutes.'
}
});
// Strict Limiter for Heavy Routes (5 req / 10 mins)
export const aiRateLimiter = rateLimit({
windowMs: 10 * 60 * 1000,
limit: 5,
store: new RedisStore({
sendCommand: (...args) => redisClient.sendCommand(args),
}),
message: {
success: false,
error: 'Limit reached for AI requests. Please wait 10 minutes.'
}
});
- Applying to Routes (routes/aiRoutes.js)
JavaScript
import express from 'express';
import { globalRateLimiter, aiRateLimiter } = '../middlewares/rateLimiter.js';
const router = express.Router();
// Apply global limit to all endpoints
router.use(globalRateLimiter);
// Protect heavy AI route with strict limit
router.post('/upload-resume-and-analyze', aiRateLimiter, (req, res) => {
res.status(202).json({
success: true,
message: 'Request accepted for background processing.'
});
});
export default router;
What I Learned:-
1.Stop requests early: Blocking bad traffic at the middleware layer saves database reads and server CPU cycles.
2.Redis is fast: Checking limits in Redis takes less than 1ms.
3.Remember trust proxy: If hosting on Render or behind Nginx, add app.set('trust proxy', 1) in Express so it reads the user's real IP instead of the load balancer IP.
Links & Live Demo
💻 GitHub Repository: https://github.com/nikhilsingh2764/invoice-Genrator
🚀 Live API Endpoint: https://invoice-backend-drqr.onrender.com
📑 Postman Collection: https://www.postman.com/technical-physicist-35686083-s-team/workspace/invoice-generator-api/collection/39798617-83cff721-5ce7-4e00-ba58-0d49017d3f39?action=share&creator=39798617
📑Postman Collection:https://www.postman.com/technical-physicist-35686083-s-team/workspace/invoice-generator-api/collection/39798617-83cff721-5ce7-4e00-ba58-0d49017d3f39?action=share&creator=39798617
🚀 Live API Endpoint: https://invoice-backend-drqr.onrender.com/
Top comments (0)