rate-limit-redis
A Redis-backed store for express-rate-limit that shares rate limit counters across multiple Node.js server instances.
Repository Health
Technical Analysis
rate-limit-redis implements the Store interface required by the express-rate-limit middleware, backing it with Redis (or Redis-protocol-compatible servers like Redict and Valkey) instead of in-memory storage. This lets rate limit counts stay consistent across horizontally scaled apps, multiple processes, and load-balanced servers, where each instance would otherwise track its own separate count.
Rather than depending on a specific Redis client, the library takes a sendCommand (or sendCommandCluster for cluster-aware routing) callback supplied by the consumer, so it works with node-redis, ioredis, or any client capable of sending raw commands. Internally it loads two small Lua scripts onto the Redis server and executes them atomically via EVALSHA, avoiding the race conditions a naive GET-then-SET/INCR sequence would introduce under concurrent requests, with automatic script-reload retries if Redis reports NOSCRIPT.
What You Get
- A
RedisStoreclass implementing express-rate-limit’sStoreinterface, ready to pass to thestoreoption ofrateLimit() - Atomic increment-and-check semantics via server-side Lua scripts (
EVALSHA), avoiding race conditions between concurrent requests for the same key - Client-agnostic design via a
sendCommand/sendCommandClustercallback, with documented adapters for node-redis, ioredis, handy-redis, tedis, redis-fast-driver, yoredis, and noderis - Built-in Redis Cluster support through
sendCommandCluster, which carries key and read/write routing details to the correct cluster node - Automatic reload-and-retry when a
NOSCRIPTerror indicates the cached Lua script was evicted from Redis - Dual ESM/CJS builds with full TypeScript type definitions, including exported types for the command-sending callbacks
Common Use Cases
- Rate limiting Express APIs that run as multiple processes or containers behind a load balancer, where in-memory counters would diverge per instance
- Sharing rate limit state across a PM2 cluster-mode or Kubernetes deployment of the same Node.js service
- Adding request throttling to an app that already runs Redis, Redict, or Valkey for caching, without introducing a second piece of infrastructure
- Rate limiting in front of a Redis Cluster, using
sendCommandClusterto route increment/get commands to the correct shard
Under The Hood
Architecture
The package is a thin, single-responsibility module: source/types.ts defines the Options/SendCommandFn contracts, source/scripts.ts holds two whitespace-normalized Lua scripts (increment, get), source/lib.ts implements the RedisStore class against express-rate-limit’s Store interface, and source/index.ts re-exports everything as the public API. The constructor validates that exactly one of sendCommand/sendCommandCluster is supplied (throwing otherwise) and normalizes both into a single internal sendCommand shape; init() eagerly loads both Lua scripts via SCRIPT LOAD and stores their SHA1s as promises so calls made before loading completes simply await the same promise. increment/get execute EVALSHA against those scripts, with a isNoScriptError type-guard and reload-and-replay path if Redis reports the script was evicted. Because every operation funnels through the single injected command function, that callback is the one abstraction the whole class depends on — swapping or breaking it affects every method uniformly.
Tech Stack
Written in TypeScript 5.2 against a shared @express-rate-limit/tsconfig, built with pkgroll (esbuild-based) into dual ESM (.mjs)/CJS (.cjs) bundles with matching .d.mts/.d.cts type declarations, per the package’s exports map. express-rate-limit (>=8.6.0) is a peer dependency, not bundled. There are no runtime dependencies at all — the library intentionally avoids depending on any specific Redis client, instead accepting a caller-supplied command function. The dev/test stack uses Jest 29 with ts-jest and ioredis-mock for Redis-free tests, xo (an ESLint preset) plus a shared Prettier config for linting/formatting, and husky/lint-staged for pre-commit enforcement, with CI defined in .github/workflows/ci.yaml.
Code Quality
A single test file (test/store-test.ts, 457 lines, 28 cases) exercises get/increment/decrement/resetKey/init, both cluster and non-cluster configuration, key prefixing, and the NOSCRIPT retry path against ioredis-mock, which is proportionate coverage for the module’s small surface. Error handling is explicit and typed: the constructor throws TypeError/Error for missing, dual, or removed configuration options, and parseScriptResponse throws on malformed Redis replies rather than failing silently; isNoScriptError uses a type predicate to safely narrow a caught unknown before pattern-matching its message. Naming is consistent and descriptive throughout (retryableIncrement, prefixKey, loadIncrementScript), and the codebase shows no use of any in the reviewed source files. Linting (xo) and formatting are enforced both locally via lint-staged/husky and in CI.
API Design
The public surface is essentially one class, RedisStore, that satisfies express-rate-limit’s Store interface, so wiring it in is typically a two-line change regardless of which Redis client the consumer already runs. Rather than depending on and re-exporting a specific client, the library asks for a thin sendCommand/sendCommandCluster raw-command function, and the README documents exact one-line adapters for six different Redis client libraries plus a dedicated cluster-routing path carrying key/isReadOnly/command metadata. Exported types (RedisReply, SendCommandClusterDetails) give consumers full inference when writing their adapter. The pattern itself is a standard store-adapter shape rather than a novel one, but it is executed with unusually broad client compatibility and clean cluster support for a package this size.
Used by 6 apps in this directory
Flowise
Developer Tools · Automation · No Code Platforms
Drag-and-drop visual builder for AI agents, RAG pipelines, and multi-agent systems—deploy anywhere in minutes.
Helicone
Monitoring · AI Development · Analytics
An open-source AI gateway and LLM observability platform that routes requests to 100+ models while logging cost, latency, and full traces for every call.
Kutt
Analytics · Marketing
Self-hosted URL shortener with custom domains, per-link analytics, and zero build step required.
LibreChat
Developer Tools · AI Assistants
Unite every major AI model in one self-hosted chat platform with agents, code execution, MCP tools, and enterprise authentication.
Omnivore
Knowledge Management · Bookmarks Archiving · Note Taking
Self-hosted read-it-later platform with highlights, newsletters, PDFs, and seamless Obsidian and Logseq integration.
Parse Server
Developer Tools · Databases
Self-hosted Backend-as-a-Service for Node.js with REST, GraphQL, real-time Live Query, cloud code, and pluggable adapters for any infrastructure.