Express.js Rate Limiter

raw JSON →
1.3.1 verified Thu Apr 23 auth: no javascript

express-rate-limiter is a middleware for Express.js applications designed to control and limit incoming requests based on user IP addresses. It implements a dual-tier rate limiting strategy: an 'inner limit' to prevent rapid-fire requests (hammering) and an 'outer limit' to guard against general overuse. The current stable version is 1.3.1. While the package previously removed external dependencies for its storage mechanism, it now primarily utilizes an in-memory store, with a roadmap item to support pluggable database solutions like Redis. Key differentiators include its configurable dual-limit approach and automatic inclusion of standard X-RateLimit and Retry-After HTTP headers in responses when limits are exceeded. Releases appear somewhat irregular but indicate active maintenance through minor versions.

error TypeError: Cannot read properties of undefined (reading 'middleware')
cause The `limiter` instance was not properly initialized or is `undefined` when `limiter.middleware()` is called.
fix
Ensure var limiter = new Limiter({ db : new MemoryStore() }); is executed before app.use(limiter.middleware()); or similar calls.
error Error: Missing db store in Limiter options.
cause The `Limiter` constructor was called without providing a `db` option.
fix
Initialize the limiter with a database store, typically new Limiter({ db: new MemoryStore() }); or your custom store implementation.
error TypeError: Limiter is not a constructor
cause Incorrect import statement for `Limiter` in an ESM context, trying to use named import for a default export, or a CommonJS `require` is used in an ESM-only file.
fix
For ESM, use import Limiter from 'express-rate-limiter';. For CommonJS in Node.js, use const Limiter = require('express-rate-limiter');.
breaking Version 1.0.0 and 0.8.0 introduced significant refactoring to a plugin-based system for storage and removed `Memory-Cache` as a dependency. Users upgrading from pre-0.8.0 versions relying on the old caching mechanism or custom store implementations will need to update their code to conform to the new `store.js` interface.
fix Review the `lib/store.js` interface and adapt any custom database implementations. Ensure `new Limiter({ db: new MemoryStore() })` or a custom store adhering to the new interface is passed during initialization.
gotcha The `Limiter` constructor requires a `db` option to be explicitly provided; it does not have a default value. Failing to provide a database store (e.g., `new MemoryStore()`) will result in runtime errors.
fix Always initialize `Limiter` with a database store, for example: `new Limiter({ db : new MemoryStore() })`.
breaking In version 0.6.0, the `Retry-After` header's value was fixed to comply with HTTP guidelines. If previous client-side logic relied on the non-compliant value, it might behave differently after upgrading.
fix Clients should re-validate their handling of the `Retry-After` header to ensure it correctly interprets the HTTP-compliant value.
gotcha The `pathLimiter` option, when enabled, prefixes the IP with a path for rate limiting, but if the `path` option is not explicitly set, the path will be read dynamically from the request. This can lead to unexpected rate limiting behavior if `path` is not consistently defined or if path segments vary.
fix If `pathLimiter: true`, ensure a consistent `path` is provided either globally or per middleware, or understand that limits will be distinct for each unique request path segment.
npm install express-rate-limiter
yarn add express-rate-limiter
pnpm add express-rate-limiter

This quickstart demonstrates how to set up `express-rate-limiter` with its default `MemoryStore`, applying rate limiting to both specific routes and globally in an Express application, including custom settings per middleware.

import Limiter from 'express-rate-limiter';
import MemoryStore from 'express-rate-limiter/lib/memoryStore';
import express from 'express';

const app = express();

// Create a new Limiter instance, specifying the database store
const limiter = new Limiter({
  db: new MemoryStore(),
  innerLimit: 5,         // Allow 5 calls per 1.5 seconds (default)
  outerLimit: 100,       // Allow 100 calls per 2 minutes (default)
  innerTimeLimit: 1500,  // 1.5 seconds
  outerTimeLimit: 120000 // 2 minutes
});

// Apply the rate limiter middleware to a specific route
app.post('/api/data', limiter.middleware({ innerLimit: 10, headers: true }), (req, res) => {
  res.status(200).send('Data successfully processed.');
});

// Apply the rate limiter globally
app.get('/public', limiter.middleware(), (req, res) => {
  res.status(200).send('Public data accessible.');
});

// Start the server
const PORT = process.env.PORT ?? 3000;
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});