Rate Limiting in JavaScript (Node.js): Protecting Your API with express-rate-limit
In modern web development, protecting server applications from excessive load and malicious attacks is a critical task. One of the most effective protection mechanisms is rate limiting. This approach allows you to control the number of requests a client (IP address, user, or session) can send to the server within a specific time period. In the Node.js and Express ecosystem, the most popular and reliable library for implementing rate limiting is express-rate-limit.
What is rate limiting and why is it needed?
Rate limiting is a technique used to manage network traffic. It prevents abuse of server resources by protecting against:
- DoS/DDoS attacks — mass requests aimed at taking the server offline.
- Brute-force attacks — guessing passwords or access tokens.
- Web scraping — automated data collection from a website.
- Excessive API usage — when one client consumes more resources than allowed.
Without rate limiting, your application can become vulnerable and unstable, especially as your user base grows.
The express-rate-limit Library
express-rate-limit is a middleware for Express.js that provides a simple and flexible way to limit requests. It integrates into the request processing chain and automatically tracks the number of requests from each client. The library supports various stores (in-memory, Redis, MongoDB, and others), making it scalable for distributed systems.
Installation
To get started, install the library via npm:
npm install express-rate-limitBasic Usage
The simplest example is limiting all application routes to 100 requests per hour from a single IP address:
const rateLimit = require('express-rate-limit');const express = require('express');const app = express();
// Create a limiterconst limiter = rateLimit({ windowMs: 60 * 60 * 1000, // 1 hour max: 100, // maximum 100 requests message: 'Too many requests from your IP. Please try again later.', standardHeaders: true, // Return standard RateLimit-* headers legacyHeaders: false, // Disable X-RateLimit-* headers});
// Apply to all routesapp.use(limiter);
app.get('/', (req, res) => { res.send('Home page');});
app.listen(3000, () => { console.log('Server running on port 3000');});Configuration for Different Routes
Often, different endpoints require different limits. For example, a login route can have a stricter limit:
const rateLimit = require('express-rate-limit');
// Strict limit for /api/loginconst loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 5, // 5 login attempts message: { error: 'Too many login attempts. Try again in 15 minutes.', retryAfter: 15 }, keyGenerator: (req) => { // Use IP + username for more precise blocking return req.ip + req.body.username; }});
app.post('/api/login', loginLimiter, (req, res) => { // Authentication logic res.send('Login successful');});
// Soft limit for public APIconst apiLimiter = rateLimit({ windowMs: 60 * 1000, // 1 minute max: 30, // 30 requests per minute message: 'API request limit exceeded.'});
app.use('/api/', apiLimiter);Advanced Features
Custom Stores
By default, data is stored in the process memory. For production, we recommend