Skip to content

handler

The HTTP request handler factory for ergo’s two-accumulator model. Creates domain accumulator and response accumulator per request, runs the composed pipeline, catches unexpected errors, and calls send() exactly once. This is the standalone equivalent of ergo-router’s auto-wrap.

Pipeline stage: Execution (wraps the entire pipeline)

import {handler} from '@centralping/ergo';
Argument Type Description
pipeline function Required. Composed middleware pipeline
Option Type Default Description
debug boolean false Enable pipeline debug tracing. When true, responseAcc._trace is initialized with {steps, breakAt} before the pipeline runs. On error responses (≥ 400), _trace appears as an RFC 9457 extension member.
onResponse function Post-send lifecycle observation hook. Called after send() completes with (req, res, responseInfo, domainAcc). Errors thrown by the hook are swallowed — the hook cannot affect the response. Async hooks are awaited.
redactHeaders Set<string> See below Header names to redact in the onResponse hook’s responseInfo.headers snapshot. Matching headers are replaced with '[REDACTED]'. Pass an empty Set to disable redaction. Shares the same default set as logger.
redactErrors boolean true Control whether caught 5xx exception messages appear in the RFC 9457 response detail field. When true, detail is set to generic status text. When false, err.message is passed through for 5xx only — 4xx always uses generic text. Stack traces are never exposed. Only set to false in development.
timing boolean | object false Inject an X-Response-Time header measuring the full request lifecycle (pipeline + error handling + send). Pass true for defaults (x-response-time header, 3 decimal places), or {header?: string, precision?: number} for custom configuration. Zero overhead when disabled. For router-level timing, see Response Timing.
prettify boolean false Pretty-print JSON output (forwarded to send)
vary string[] ['Accept'] Vary header values to append (forwarded to send)
etag boolean true Generate ETags and evaluate conditional headers (forwarded to send)
prefer boolean false Read domainAcc.prefer for RFC 7240 return=minimal / return=representation (forwarded to send)
paginate boolean false Read domainAcc.paginate and responseAcc.paginate to auto-generate RFC 8288 Link headers and X-Total-Count for paginated responses (forwarded to send)
envelope boolean | function false Wrap 2xx Object bodies in a response envelope (forwarded to send)
errorFormatter function Custom error body formatter for 4xx/5xx responses. Receives the RFC 9457 Problem Details object and {requestId, statusCode, method} context. Return value becomes the response body as application/json instead of application/problem+json. (forwarded to send)
responseSchema Record<number|string, object> Map of status code to JSON Schema for response body projection (forwarded to send)

authorization, proxy-authorization, cookie, set-cookie

Returns an async handler (req, res) => void suitable for http.createServer().

Status Condition
500 Internal Server Error Uncaught pipeline error (detail is redacted for security)
  • Sets responseAcc.statusCode = 500 for uncaught pipeline errors (unless a timeout or earlier pipeline break already set the status)
  • Attaches instance from the X-Request-Id response header
  • Emits error on res if error listeners are present
  • Records the exception on the OTEL span (if tracing is configured)
  • If send() itself throws: emits the error on res (if listeners are present), records the exception on the OTEL span, and — if the response has not already been committed — sets responseAcc.statusCode = 500 and ends the response with res.statusCode = 500; res.end()

The onResponse option registers a post-send observation callback. It fires after the HTTP response has been written — the hook cannot alter headers, status, or body.

Field Type Description
statusCode number Final HTTP status code
headers object Response headers snapshot (from res.getHeaders()), with sensitive headers redacted per the redactHeaders option
method string Request HTTP method
url string Request URL
bodySize number | undefined Content-Length value; undefined for stream bodies
duration number Pipeline execution time in milliseconds (from performance.now())
import http from 'node:http';
import {handler, compose} from '@centralping/ergo';
const pipeline = compose(
(req, res, acc) => ({
response: {statusCode: 200, body: {ok: true}},
}),
);
const server = http.createServer(
handler(pipeline, {
onResponse: (req, res, responseInfo, domainAcc) => {
console.log(JSON.stringify({
method: responseInfo.method,
url: responseInfo.url,
status: responseInfo.statusCode,
duration: responseInfo.duration,
user: domainAcc.auth?.identity,
}));
},
}),
);
import http from 'node:http';
import {
handler, compose, logger, authorization, body,
} from '@centralping/ergo';
const pipeline = compose(
logger(),
authorization({strategies}),
body(),
(req, res, acc) => ({
response: {
body: processRequest(acc),
statusCode: 200,
},
}),
);
const server = http.createServer(
handler(pipeline, {debug: true, prettify: true}),
);
server.listen(3000);
  • Custom Middleware — Writing middleware that fits ergo’s accumulator-based return-value contract
  • Debug Tracing — Finding which middleware rejected a request using pipeline debug mode

See the auto-generated handler API docs.