Redis Store for Express Rate Limit
rate-limit-redis is a Redis-backed storage engine designed for the `express-rate-limit` middleware, enabling distributed rate limiting across multiple application instances. It is currently at stable version 4.3.1, with frequent minor and patch releases, as indicated by the recent changelog entries, reflecting an active maintenance schedule. This library supports popular Redis clients such as `node-redis` and `ioredis`, and also explicitly lists compatibility with `redict` and `valkey`, offering flexibility in deployment. A key differentiator is its flexible `sendCommand` abstraction, which allows seamless integration with various Redis client libraries by adapting their specific command execution functions. It requires Node.js 16 or above and Redis 2.6.12 or above for operation. The project maintains an active development status, ensuring compatibility with the latest `express-rate-limit` versions and modern Node.js environments.
Common errors
-
TypeError: client.connect is not a function
cause The `node-redis` client (created with `createClient()`) requires an explicit `await client.connect()` call before it can be used to send commands.fixEnsure you call `await client.connect()` after creating your Redis client instance and before passing it to the `RedisStore` constructor. -
Error: require() of ES Module ... not supported.
-
SyntaxError: Cannot use import statement outside a module
cause This error typically occurs when mixing CommonJS (`require`) and ES Module (`import`) syntax, or when an ES Module is imported in a CommonJS context without proper transpilation or configuration.fixFor ES Modules, set `"type": "module"` in your `package.json` and use `import`. For CommonJS, ensure `"type": "commonjs"` (or no `type` field) and use `require()`. Ensure bundlers or TypeScript configurations handle module resolution correctly. -
Argument of type '(command: string, ...args: string[]) => Promise<RedisReply>' is not assignable to parameter of type '(...args: string[]) => number | Promise<number>'
cause When using TypeScript, the `sendCommand` function in `RedisStore` expects a return type of `Promise<number>` or `number`. If your Redis client's command function returns a different type (e.g., `Promise<RedisReply>` from `ioredis`'s `call` method), a type mismatch occurs.fixAdjust the `sendCommand` function's signature or cast its return value to `Promise<number>` or `number` to match the expected type. For `ioredis`, you might cast `client.call(...) as Promise<number>`. -
TypeError: Cannot read properties of undefined (reading 'sendCommand')
cause The `sendCommand` function was not correctly provided or it's attempting to access a method on an uninitialized or incorrectly configured Redis client instance within the `RedisStore` options.fixVerify that your Redis client object is properly initialized and connected, and that the `sendCommand` function within `RedisStore`'s options correctly references a valid command sending method on that client (e.g., `client.sendCommand(args)` or `client.call(command, ...args)`).
Warnings
- breaking Version 4.0.0 dropped support for Node.js 14. Projects must now use Node.js 16 or newer to ensure compatibility.
- breaking Version 4.0.0 introduced support for `express-rate-limit` v7. Ensure your `express-rate-limit` dependency is updated to a compatible version (v7 or higher) to avoid potential issues.
- gotcha This package requires a Redis server version 2.6.12 or above. Using an older Redis version may lead to unexpected behavior or errors.
- gotcha Prior to v4.1.1, the `store.get()` function would return `NaN` if no hits were stored for a client, instead of the expected `0`. This could cause issues in logic dependent on the return value.
Install
-
npm install rate-limit-redis -
yarn add rate-limit-redis -
pnpm add rate-limit-redis
Imports
- RedisStore
const RedisStore = require('rate-limit-redis').RedisStoreimport { RedisStore } from 'rate-limit-redis' - RedisStore
import type { RedisStore } from 'rate-limit-redis' - RedisReply
import type { RedisReply } from 'rate-limit-redis'
Quickstart
import { rateLimit } from 'express-rate-limit';
import { RedisStore } from 'rate-limit-redis';
import { createClient } from 'redis';
import express from 'express';
const app = express();
async function setupRateLimiter() {
// Create a `node-redis` client
const client = createClient({
url: process.env.REDIS_URL ?? 'redis://localhost:6379'
});
// Then connect to the Redis server
client.on('error', (err) => console.error('Redis Client Error', err));
await client.connect();
console.log('Connected to Redis');
// Create and use the rate limiter
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per window
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
// Redis store configuration
store: new RedisStore({
sendCommand: (...args: string[]) => client.sendCommand(args),
}),
});
app.use(limiter);
app.get('/', (req, res) => {
res.send('Hello, you are rate-limited!');
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
}
setupRateLimiter().catch(console.error);