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'); }}); Also in library
QR Code Generator in Kotlin: ZXing Library and Implementation Tips
Swagger/OpenAPI Generation for C# (.NET): Swashbuckle and NSwag
Moq for C# (.NET): Complete Guide with Examples
Working with Markdown in Java: Libraries, Parsing, and Conversion