Rate Limiting in Node.js: A Complete Guide to API Protection and Load Management

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Rate Limiting in Node.js: Protecting APIs from Overloads and Attackers



Rate limiting is a critically important mechanism for any web application built on Node.js. It allows you to control the number of requests a client can send to a server within a specific time period. Without rate limiting, your API is vulnerable to DDoS attacks, accidental overloads, and excessive resource consumption.



Why is Rate Limiting Needed?

  • Brute-force protection: Prevents multiple login attempts.
  • DDoS prevention: Limits the impact of denial-of-service attacks.
  • Fair resource distribution: Guarantees equal access for all users.
  • Cost savings: Reduces load on servers and databases.
  • SLA compliance: Meeting service level agreements.


Core Rate Limiting Algorithms

There are several approaches to implementing rate limiting. Let's look at the most popular ones:



1. Sliding Window

The most common algorithm. Requests are grouped into time windows (e.g., 1 minute). If the number of requests exceeds the limit, subsequent requests are rejected until a new window begins.



2. Token Bucket

A virtual "bucket" contains tokens. Each request consumes one token. Tokens are replenished at a fixed rate. If the bucket is empty, the request is rejected.



3. Leaky Bucket

Requests enter a queue and are processed at a constant rate. If the queue overflows, requests are discarded.



Libraries for Rate Limiting in Node.js



1. express-rate-limit

The most popular library for Express.js. Simple to use and configurable.



const rateLimit = require('express-rate-limit');

// Create middleware for rate limitingconst limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // max 100 requests per window message: 'Too many requests, please try again later.', standardHeaders: true, // Return RateLimit-* headers legacyHeaders: false, // Disable X-RateLimit-* headers});

// Apply to all routesapp.use(limiter);

// Or only to a specific routeapp.use('/api/login', limiter);


2. bottleneck

A versatile library that works with any asynchronous operations. Supports Token Bucket and Leaky Bucket algorithms.



const Bottleneck = require('bottleneck');

// Create a limiter: 5 requests per secondconst limiter = new Bottleneck({ minTime: 200, // 200 ms between requests maxConcurrent: 5, // max 5 concurrent requests reservoir: 100, // initial number of tokens reservoirRefreshAmount: 100, reservoirRefreshInterval: 60 * 1000, // replenish once per minute});

// Wrap a function with the limiterasync function fetchData(url) { const response = await fetch(url); return response.json();}

const limitedFetch = limiter.wrap(fetchData);

// Usageconst data = await limitedFetch('https://api.example.com/data');


3. rate-limiter-flexible

A flexible library with support for Redis, MongoDB, Memcached, and other stores. Ideal for distributed systems.



const { RateLimiterRedis } = require('rate-limiter-flexible');const Redis = require('ioredis');

const redisClient = new Redis();

const rateLimiter = new RateLimiterRedis({ storeClient: redisClient, keyPrefix: 'middleware', points: 10, // 10 requests duration: 1, // per 1 second});

app.use(async (req, res, next) => { try { await rateLimiter.consume(req.ip); next(); } catch (rejRes) { res.status(429).send('Too many requests'); }});

Recommendations