Node.js cookbook · Beginner

Rate limit an Express API across instances

Throttle login attempts and API calls per client, shared across all Node processes.

Approach

Use express-rate-limit with a Redis store (rate-limit-redis) so counts are shared across processes and servers. Apply a strict limiter to auth routes and a looser one globally, key by user/API key when authenticated, and enable standard RateLimit headers. Configure trust proxy correctly so client IPs are real.

Solution

import express from "express";
import { rateLimit } from "express-rate-limit";
import { RedisStore } from "rate-limit-redis";
import { createClient } from "redis";

const redis = createClient({ url: process.env.REDIS_URL });
await redis.connect();

const store = (prefix: string) =>
  new RedisStore({ prefix, sendCommand: (...args: string[]) => redis.sendCommand(args) });

const app = express();
app.set("trust proxy", 1);                  // one proxy hop (e.g. the load balancer)

app.use("/api", rateLimit({
  windowMs: 60_000,
  limit: 300,
  standardHeaders: "draft-8",
  legacyHeaders: false,
  keyGenerator: (req) => req.get("x-api-key") ?? req.ip!,
  store: store("rl:api:"),
}));

app.post("/api/auth/login", rateLimit({
  windowMs: 15 * 60_000,
  limit: 10,
  skipSuccessfulRequests: true,             // count only failed attempts
  store: store("rl:login:"),
}), loginHandler);

Pitfalls

  • Memory store with multiple processes (cluster/PM2/Kubernetes).
  • trust proxy set to true blindly (clients can spoof X-Forwarded-For).
  • Keying by IP only for APIs used from shared NATs.

Variations

  • Fastify: @fastify/rate-limit with Redis.
  • Edge/gateway rate limiting (Cloudflare, API Gateway) for coarse protection.

Further reading: express-rate-limit

Reference: https://express-rate-limit.mintlify.app/overview

More Node.js recipes