Script Valley
Express.js: APIs and Middleware
Advanced Middleware PatternsLesson 5.1

How to build a rate limiting middleware from scratch

rate limiting concept, in-memory request tracking, IP-based limits, window reset, 429 Too Many Requests, Retry-After header, sliding vs fixed window, express-rate-limit library

Rate Limiting Middleware

Rate limiting prevents API abuse by capping how many requests a client can make in a time window. Every production API needs it.

In-memory rate limiter

const requestCounts = new Map(); // IP -> { count, resetTime }

function rateLimiter(maxRequests = 100, windowMs = 60 * 1000) {
  return (req, res, next) => {
    const ip = req.ip;
    const now = Date.now();
    const record = requestCounts.get(ip);

    if (!record || now > record.resetTime) {
      requestCounts.set(ip, { count: 1, resetTime: now + windowMs });
      return next();
    }

    if (record.count >= maxRequests) {
      const retryAfter = Math.ceil((record.resetTime - now) / 1000);
      res.set('Retry-After', retryAfter);
      return res.status(429).json({
        error: 'Too many requests',
        retryAfter
      });
    }

    record.count++;
    next();
  };
}

app.use(rateLimiter(100, 60000)); // 100 req/min
app.use('/auth', rateLimiter(5, 60000)); // stricter on auth routes

For production, use the express-rate-limit package with a Redis store โ€” in-memory counters reset on server restart and don't work across multiple processes. The built-in version here is for single-process development.

Up next

Input sanitization middleware โ€” preventing injection attacks

Sign in to track progress

How to build a rate limiting middleware from scratch โ€” Advanced Middleware Patterns โ€” Express.js: APIs and Middleware โ€” Script Valley โ€” Script Valley