Skip to content

Custom Headers

You need to add custom response headers — timing headers, correlation IDs, cache directives, or API-specific metadata — from middleware or execute handlers without calling res.setHeader() directly.

Return headers as [name, value] tuples in the response.headers array. send() applies them to the response before writing the body.

import { compose } from "@centralping/ergo";
const pipeline = compose(
(req, res, acc) => ({
response: {
body: { ok: true },
headers: [
["X-Custom-Header", "my-value"],
["X-Request-Start", String(Date.now())],
],
},
}),
);

For accurate response time measurement, use the logger middleware’s built-in duration field. Logger attaches to res.on('finish') and records total time from middleware entry to response completion:

import { compose, logger } from "@centralping/ergo";
const pipeline = compose(
[logger({
log: (info) => console.log(`${info.method} ${info.url} ${info.duration}ms`),
}), "log"],
// ... remaining middleware
);

If you need response time logging without the logger middleware, attach a finish listener before calling the handler:

import http from "node:http";
import { handler, compose } from "@centralping/ergo";
const pipeline = compose(/* ... */);
const app = handler(pipeline);
http.createServer((req, res) => {
const start = performance.now();
res.on("finish", () => {
console.log(`${req.method} ${req.url} ${(performance.now() - start).toFixed(2)}ms`);
});
app(req, res);
}).listen(3000);

Set a header value to undefined to clear it from the response:

(req, res, acc) => ({
response: {
headers: [["X-Powered-By", undefined]],
},
});

Headers from the response accumulator are applied by send() after the pipeline completes. send() iterates the headers array and calls res.setHeader(name, value) for each tuple, or res.clearHeader(name) when the value is undefined.

This design means:

  1. Multiple middleware can contribute headers — each returns its own headers array, and the pipeline merges them into the response accumulator.
  2. Later middleware can override earlier headers — if two middleware set the same header name, the last one wins (standard HTTP semantics).
  3. No res mutation in middleware — middleware never calls res.setHeader() directly. All header contributions flow through the accumulator, keeping the pipeline pure and testable.

ergo includes middleware that manages common headers automatically:

MiddlewareHeaders Set
security-headersContent-Security-Policy, X-Frame-Options, etc.
cache-controlCache-Control
corsAccess-Control-* headers
compressContent-Encoding, Vary

Use custom headers for application-specific metadata that these middleware do not cover.