How I Protected My Express API from Spam and High AI Costs Using Redis A developer implemented Redis-based rate limiting to protect an Express API from spam and high AI costs. The solution uses a centralized Redis store to track request counts across multiple server instances, with global and strict limits for normal and AI-heavy routes. The developer highlighted that in-memory rate limiting causes memory leaks and breaks when scaling, while Redis provides a fast, shared counter. 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. At first, I thought about saving request counts in a simple JavaScript object: js // ❌ 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 Client Requests │ ▼ Cloud Load Balancer │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ Express Node 1 Express Node 2 Express Node 3 │ │ │ └───────────────┼───────────────┘ ▼ Central Redis Store Checks Request Limits 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 . js 1. Redis Connection config/redis.js 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; js 2. Middleware Setup middlewares/rateLimiter.js import { rateLimit } from 'express-rate-limit'; import { RedisStore } = require 'rate-limit-redis' ; // or 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.' } } ; python 3. Applying to Routes routes/aiRoutes.js import express from 'express'; import { globalRateLimiter, aiRateLimiter } from '../middlewares/rateLimiter.js'; const router = express.Router ; // Apply global limit to all endpoints in this router 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. 💻 GitHub: https://github.com/nikhilsingh2764/invoice-Genrator https://github.com/nikhilsingh2764/invoice-Genrator 🚀 Live API: https://invoice-backend-drqr.onrender.com/ https://invoice-backend-drqr.onrender.com/ 📑 Postman Collection: https://www.postman.com/technical-physicist-35686083-s-team/invoice-generator-api/collection/39798617-83cff721-5ce7-4e00-ba58-0d49017d3f39?action=share&creator=39798617 https://www.postman.com/technical-physicist-35686083-s-team/invoice-generator-api/collection/39798617-83cff721-5ce7-4e00-ba58-0d49017d3f39?action=share&creator=39798617