Record Request Timing Metrics
We have no per-endpoint latency data, so slow routes only show up as customer complaints. This adds middleware that times every request and records it in a histogram labeled by method, route pattern, and status code. Unmatched paths share one "unmatched" label so raw URLs never become label values. The existing /metrics endpoint exposes it.
Aapi/middleware/requestTiming.ts1 of 3
Files Changed
api/middleware/
api/
api/middleware/requestTiming.ts
TypeScript · Click a line number to comment · Tap a line to comment@@ -0,0 +1,25 @@
Added: import type { NextFunction, Request, Response } from "express";
Added: import { Histogram } from "prom-client";
Added:
Added: export const requestDuration = new Histogram({
Added: name: "http_request_duration_ms",
Added: help: "HTTP request duration in milliseconds",
Added: labelNames: ["method", "route", "status"],
Added: buckets: [5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000],
Added: });
Added:
Added: export function requestTiming(req: Request, res: Response, next: NextFunction) {
Added: const start = process.hrtime.bigint();
Added: res.on("finish", () => {
Added: const ms = Number(process.hrtime.bigint() - start) / 1e6;
Added: requestDuration.observe(
Added: {
Added: method: req.method,
Added: route: req.route?.path ?? "unmatched",
Added: status: String(res.statusCode),
Added: },
Added: ms,
Added: );
Added: });
Added: next();
Added: }
api/app.ts
TypeScript · Click a line number to comment · Tap a line to comment@@ -1,18 +1,20 @@
import express from "express";
import helmet from "helmet";
import { register } from "prom-client";
Added: import { requestTiming } from "./middleware/requestTiming";
import { ordersRouter } from "./routes/orders";
import { usersRouter } from "./routes/users";
export const app = express();
Added: app.use(requestTiming);
app.use(helmet());
app.use(express.json());
app.use("/users", usersRouter);
app.use("/orders", ordersRouter);
app.get("/metrics", async (_req, res) => {
res.set("Content-Type", register.contentType);
res.end(await register.metrics());
});
api/middleware/requestTiming.test.ts
TypeScript · Click a line number to comment · Tap a line to comment@@ -0,0 +1,21 @@
Added: import express from "express";
Added: import request from "supertest";
Added: import { register } from "prom-client";
Added: import { requestDuration, requestTiming } from "./requestTiming";
Added:
Added: describe("requestTiming", () => {
Added: beforeEach(() => requestDuration.reset());
Added:
Added: it("records the route pattern, method, and status", async () => {
Added: const app = express();
Added: app.use(requestTiming);
Added: app.get("/orders/:id", (_req, res) => res.sendStatus(204));
Added:
Added: await request(app).get("/orders/42").expect(204);
Added:
Added: const metrics = await register.getSingleMetricAsString("http_request_duration_ms");
Added: expect(metrics).toContain('route="/orders/:id"');
Added: expect(metrics).toContain('method="GET"');
Added: expect(metrics).toContain('status="204"');
Added: });
Added: });