express-rate-limit

Basic IP rate-limiting middleware for Express to throttle repeated requests and protect public APIs and sensitive endpoints.

Library
npm
v8.7.1
3,313 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
85 /100 Excellent
Development Activity 92
Maintenance 84
Community 64
Maturity 60
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
89 /100 Excellent
Architecture 88
Code Quality 90
Innovation 88
Learning Curve 90

express-rate-limit is a small, dependency-light middleware for the Express web framework that caps how many requests a client can make within a configurable time window. Drop it into an app to shield public APIs, login flows, and password-reset endpoints from brute-force attempts, scraping, and accidental request floods.

It ships with an in-memory store for single-process apps and a well-defined Store interface for scaling across processes with Redis, Memcached, or other backends. Response headers follow the IETF RateLimit draft standards (draft-6, draft-7, and draft-8), and nearly every option—limits, key generation, skip rules, and handlers—can be a synchronous or async function.

What You Get

  • A configurable middleware factory (rateLimit) that enforces a request limit over a windowMs time window
  • A built-in in-memory store plus a documented Store interface for Redis, Memcached, and other shared backends
  • Standards-compliant RateLimit response headers (IETF draft-6, draft-7, and draft-8) with legacy X-RateLimit support
  • Async-capable options for limits, key generation, skip rules, and custom limit-reached handlers
  • Exported helpers including MemoryStore, ipKeyGenerator, and SECOND/MINUTE/HOUR/DAY time constants

Common Use Cases

  • Throttling public REST APIs to prevent abuse and control infrastructure costs
  • Protecting authentication endpoints such as login and password reset against brute-force attacks
  • Rate-limiting form submissions, search, and other expensive endpoints against scraping or floods
  • Sharing hit counts across a multi-node deployment via an external Redis or Memcached store

Under The Hood

Architecture — The public entry point (source/index.ts) re-exports a single rateLimit factory from source/rate-limit.ts, alongside MemoryStore, ipKeyGenerator, and time constants. rate-limit.ts parses and normalizes options, wraps legacy callback-style stores in a promisified adapter (isLegacyStore/promisifyStore), and returns an Express RequestHandler. On each request the handler derives a client key (keyGenerator, IP-based by default via source/ip-key-generator.ts), calls store.increment(key), evaluates the hit count against the resolved limit, attaches rate-limit info to the request object, and sets standards headers from source/headers.ts before either calling next() or invoking the limit-reached handler. State lives behind the Store abstraction, with source/memory-store.ts providing the default in-process implementation.

Tech Stack — Written entirely in TypeScript (100% of the codebase), shipped as dual ESM/CJS via esbuild with types bundled by dts-bundle-generator. Runtime dependencies are minimal: debug for diagnostics and ip-address for IPv6 handling, with express declared only as a peer dependency (>= 4.11). Tooling uses Biome for linting/formatting, Jest with ts-jest for tests, Husky and lint-staged for pre-commit checks, and Mintlify for hosted docs. Targets Node >= 16.

Code Quality — The library is well-tested: test/library contains focused suites for the middleware, memory store, headers, validation, options, key generation, and logging, with the middleware suite alone exceeding 31KB, plus a separate external-integration test harness. Source files are small, single-responsibility modules with JSDoc on internal helpers, strong typing throughout (source/types.ts), and a dedicated validations.ts that proactively warns about common misconfigurations. Error handling is explicit, including a configurable passOnStoreError policy for store outages.

API Design — The developer experience is excellent. Getting started requires a single rateLimit({ windowMs, limit }) call passed to app.use(), and the option surface is broad but discoverable, with nearly every field accepting a static value or a sync/async function. Naming is consistent and intent-revealing (windowMs, limit, keyGenerator, skip, handler), readable time constants reduce magic numbers, and the built-in validation layer plus thorough hosted documentation make misconfiguration easy to catch and correct.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers