response-time
Express and Connect middleware that times each request and writes the elapsed duration to an X-Response-Time header.
Repository Health
Technical Analysis
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-Timeheader 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
headeroption, so it can coexist with reverse proxies or load balancers that set their own timing headers - Configurable
suffixoption to emit a bare number instead of anms-suffixed string - Works with any framework built on Node’s core
httpmodule, not just Express — Connect and vanillahttp.createServerhandlers 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.
Used by 2 apps in this directory
Firecrawl
AI Development · Developer Tools
Turn any website into clean, LLM-ready data with a single API call — no proxy headaches, no scraping complexity.
SillyTavern
AI Assistants
The power-user LLM frontend that unifies dozens of AI backends with a rich scripting engine, immersive Visual Novel mode, and a thriving extension ecosystem.