{"slug": "how-i-protected-my-express-api-from-spam-and-high-ai-costs-using-redis", "title": "How I Protected My Express API from Spam and High AI Costs Using Redis", "summary": "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.", "body_md": "When I was building my backend API, I realized a big problem: anyone could spam my endpoints.\n\nIf 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.\n\nTo fix this, I added **Rate Limiting**. Here is why I used Redis for it and how I set it up.\n\nAt first, I thought about saving request counts in a simple JavaScript object:\n\n``` js\n// ❌ Simple in-memory check (Not good for production)\nconst requestCounts = {};\n\napp.use((req, res, next) => {\n  const ip = req.ip;\n  requestCounts[ip] = (requestCounts[ip] || 0) + 1;\n\n  if (requestCounts[ip] > 100) {\n    return res.status(429).json({ error: \"Too many requests\" });\n  }\n  next();\n});\n```\n\nThis works locally, but has two big flaws:\n\n1)Memory Leaks: The requestCounts object keeps growing in memory forever.\n\n2)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.\n\nThe Solution: Centralized Redis Store\n\nRedis stores data in RAM outside our Node.js app. Because it is centralized, all server instances share the exact same count.\n\n```\n           [ Incoming Client Requests ]\n                     │\n                     ▼\n           [ Cloud Load Balancer ]\n                     │\n     ┌───────────────┼───────────────┐\n     ▼               ▼               ▼\n[ Express Node 1 ] [ Express Node 2 ] [ Express Node 3 ]\n     │               │               │\n     └───────────────┼───────────────┘\n                     ▼\n          [ Central Redis Store ]\n         (Checks Request Limits)\n```\n\nHow I Configured It in My Project\n\nIn my app, I use two levels of protection:\n\nGlobal Limit: 100 requests per 15 minutes for normal routes.\n\nStrict Limit: 5 requests per 10 minutes for heavy routes (like AI generation or OTP emails).\n\n``` js\n1. Redis Connection (config/redis.js)\n\nimport { createClient } from 'redis';\n\nconst redisClient = createClient({\n  url: process.env.REDIS_URL || 'redis://localhost:6379'\n});\n\nredisClient.on('error', (err) => console.error('Redis Error:', err));\nredisClient.on('connect', () => console.log('Connected to Redis'));\n\nawait redisClient.connect();\n\nexport default redisClient;\njs\n2. Middleware Setup (middlewares/rateLimiter.js)\n\nimport { rateLimit } from 'express-rate-limit';\nimport { RedisStore } = require('rate-limit-redis'); // or import { RedisStore } from 'rate-limit-redis';\nimport redisClient from '../config/redis.js';\n\n// Global Limiter (100 req / 15 mins)\nexport const globalRateLimiter = rateLimit({\n  windowMs: 15 * 60 * 1000,\n  limit: 100,\n  standardHeaders: 'draft-7',\n  legacyHeaders: false,\n  store: new RedisStore({\n    sendCommand: (...args) => redisClient.sendCommand(args),\n  }),\n  message: {\n    success: false,\n    error: 'Too many requests. Please try again in 15 minutes.'\n  }\n});\n\n// Strict Limiter for Heavy Routes (5 req / 10 mins)\nexport const aiRateLimiter = rateLimit({\n  windowMs: 10 * 60 * 1000,\n  limit: 5,\n  store: new RedisStore({\n    sendCommand: (...args) => redisClient.sendCommand(args),\n  }),\n  message: {\n    success: false,\n    error: 'Limit reached for AI requests. Please wait 10 minutes.'\n  }\n});\npython\n3. Applying to Routes (routes/aiRoutes.js)\n\nimport express from 'express';\nimport { globalRateLimiter, aiRateLimiter } from '../middlewares/rateLimiter.js';\n\nconst router = express.Router();\n\n// Apply global limit to all endpoints in this router\nrouter.use(globalRateLimiter);\n\n// Protect heavy AI route with strict limit\nrouter.post('/upload-resume-and-analyze', aiRateLimiter, (req, res) => {\n  res.status(202).json({\n    success: true,\n    message: 'Request accepted for background processing.'\n  });\n});\n\nexport default router;\n```\n\nWhat I Learned\n\n1)Stop requests early: Blocking bad traffic at the middleware layer saves database reads and server CPU cycles.\n\n2)Redis is fast: Checking limits in Redis takes less than 1ms.\n\n3)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.\n\n💻 GitHub:[https://github.com/nikhilsingh2764/invoice-Genrator](https://github.com/nikhilsingh2764/invoice-Genrator)\n\n🚀 Live API:[https://invoice-backend-drqr.onrender.com/](https://invoice-backend-drqr.onrender.com/)\n\n📑 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))", "url": "https://wpnews.pro/news/how-i-protected-my-express-api-from-spam-and-high-ai-costs-using-redis", "canonical_source": "https://dev.to/nikhil_singh_e20fff10a888/how-i-protected-my-express-api-from-spam-and-high-ai-costs-using-redis-40c", "published_at": "2026-08-09 18:24:10+00:00", "updated_at": "2026-08-09 18:47:11.288394+00:00", "lang": "en", "topics": ["developer-tools", "ai-products"], "entities": ["Redis", "Express", "rate-limit-redis", "express-rate-limit"], "alternates": {"html": "https://wpnews.pro/news/how-i-protected-my-express-api-from-spam-and-high-ai-costs-using-redis", "markdown": "https://wpnews.pro/news/how-i-protected-my-express-api-from-spam-and-high-ai-costs-using-redis.md", "text": "https://wpnews.pro/news/how-i-protected-my-express-api-from-spam-and-high-ai-costs-using-redis.txt", "jsonld": "https://wpnews.pro/news/how-i-protected-my-express-api-from-spam-and-high-ai-costs-using-redis.jsonld"}}