response-time

Express and Connect middleware that times each request and writes the elapsed duration to an X-Response-Time header.

Library
npm
v2.3.4
498stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
46/100Fair
Development Activity16
Maintenance20
Community68
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
71/100Good
Architecture72
Code Quality78
Innovation45
Learning Curve90

response-time is a small, single-purpose middleware for Node.js HTTP servers that records how long a request takes to process. It hooks into the response lifecycle using on-headers so the timer stops at the exact moment headers are written out to the client, then sets an X-Response-Time header (e.g. 2.300ms) unless one is already present.

Beyond the default header behavior, it can be called with a custom callback function instead of options, in which case it hands the raw millisecond timing to that function so it can be piped into a metrics system (StatsD, Prometheus, etc.) rather than exposed as a response header at all. It has been part of the expressjs GitHub organization for over a decade and is widely used as the standard way to add response-time visibility to Express and Connect applications.

What You Get

  • Automatic X-Response-Time header on every response, formatted in milliseconds with a configurable number of digits
  • A callback mode — pass a function instead of options and receive (req, res, time) directly for wiring into StatsD, Prometheus, or any other metrics backend
  • Configurable header name via the header option, so it can coexist with reverse proxies or load balancers that set their own timing headers
  • Configurable suffix option to emit a bare number instead of an ms-suffixed string
  • Works with any framework built on Node’s core http module, not just Express — Connect and vanilla http.createServer handlers are both supported
  • Zero-config default behavior — app.use(responseTime()) is enough to get accurate, deprecation-safe timing on every route

Common Use Cases

  • Quick performance visibility - drop responseTime() into an Express app during development to see per-request latency in the browser’s network inspector without adding a logging library
  • Feeding a metrics backend - use the callback form to forward per-route response times into StatsD or Prometheus for dashboards and alerting
  • API latency debugging - correlate slow client-perceived load times with the exact server-side processing duration reported in the header
  • Reverse-proxy header handoff - set a custom header name so response-time’s number can sit alongside a load balancer’s own X-Request-Id/timing headers without colliding

Under The Hood

Architecture The entire module lives in a single file, index.js, exporting one factory function, responseTime(options), that returns a standard (req, res, next) middleware closure. Inside that closure, process.hrtime() captures a start timestamp, and an on-headers callback registered on res captures the elapsed time at the exact moment headers are about to be flushed — decoupling the timing logic from any specific point in the request-handling chain. A small internal helper, createSetHeader, builds the header-writing function from the resolved options (digits, header, suffix), while an alternate code path lets a caller pass a plain function instead of an options object, swapping header-writing for a direct callback. This is a flat, single-responsibility design with no internal layering because the scope is intentionally narrow.

Tech Stack The module has exactly two runtime dependencies: depd (structured deprecation warnings, used for the legacy numeric-argument call signature) and on-headers (the hook that lets code run just before response headers are sent, itself a small first-party expressjs-org package). It targets plain Node.js http semantics rather than any specific framework, with engines.node >= 0.8.0 reflecting its long-lived, backward-compatible target range. Dev tooling is a classic pre-flat-config ESLint 4 setup (eslint-config-standard) plus mocha/nyc/supertest for testing and coverage.

Code Quality test/test.js is a genuinely thorough suite for the module’s size: it exercises default header output, custom digits, custom header names, the suffix toggle, the callback-mode API, and the no-double-write guard when a header is already set, all via supertest against real http servers rather than mocks. There is no TypeScript and no runtime type checking — options are read with plain typeof checks — but ESLint (Standard config) is wired into npm run lint and a GitHub Actions CI workflow (referenced by the README’s build-status badge) runs the suite. Error handling is minimal because the module’s failure surface is intentionally small: it does not throw on bad input, it just falls back to sane defaults.

API Design The public surface is a single default export with three resolvable call shapes: no arguments, an options object (digits/header/suffix), or a callback function — all inferred by typeof, so callers never need to pick a named entry point. Sensible defaults (digits: 3, header: 'X-Response-Time', suffix: true) mean the common case is app.use(responseTime()) with zero configuration, while the deprecated legacy responseTime(3) numeric-argument form is still accepted and routed through depd so consumers get a visible warning rather than a silent behavior change. The README documents every option and both call shapes with runnable examples for Express, vanilla http, and a StatsD metrics pipeline.

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

Search