Skip to content

Accumulator Reference

The two-accumulator pipeline model passes two objects through every middleware in the chain: the domain accumulator (acc) for inter-middleware data, and the response accumulator for HTTP response properties. This page is the consolidated reference for both.

Each domain-producing middleware carries an intrinsic accumulator path — a non-enumerable setPath property set by the factory at creation time. Built-in middleware can be composed as bare functions; custom middleware uses {fn, setPath} config objects. The table below maps every pipeline-builder key to its accumulator path and typed interface.

Config Key Accumulator Path (acc.*) Type Interface Pipeline Stage Notes
tracing trace TracingResult Negotiation Shortened — aligns with OpenTelemetry trace terminology
logger log LogEntry Negotiation Shortened for ergonomics
rateLimit rateLimit Negotiation
accepts accepts AcceptsResult Negotiation
preconditionRequired precondition Negotiation Shortened — the Required suffix describes the middleware’s enforcement behavior, not the accumulator value
cookie cookies CookieJar Negotiation Pluralized — the jar holds multiple cookies
url url UrlResult Negotiation
paginate paginate PaginateResult Negotiation
jsonApiQuery jsonApiQuery Negotiation
prefer prefer PreferResult Negotiation
securityHeaders security Negotiation Shortened — config key names the middleware’s concern (headers), setPath names the domain concept
cacheControl cache Negotiation Shortened — cacheControl names the HTTP directive, cache names the domain concept
csrf csrf Authorization
authorization auth AuthorizationResult Authorization Shortened for ergonomics — acc.auth is accessed frequently in execute handlers
body body BodyResult Validation
idempotency idempotency IdempotencyResult Validation
validate validation Validation Renamed — config key names the action (validate), setPath names the result (validation output)

Stored at acc.url by the url() middleware. Parsed URL components using a fast indexOf + slice parser.

interface UrlResult {
query: Record<string, string | string[]>; // flat published type; nested at runtime — see tip
pathname: string | undefined;
search: string | undefined;
}
Property Type Description
pathname string | undefined URL path before the query string
search string | undefined Raw query string including leading ?; undefined when absent
query Record<string, string | string[]> Parsed query parameters (null-prototype object); multi-value keys become arrays. Bracket notation nests null-prototype objects at runtime; the published type is a flat simplification (see tip above and url).

Full documentation →

Stored at acc.paginate by the paginate() middleware. Parsed pagination parameters — a discriminated union based on the active strategy.

type PaginateResult =
| {strategy: 'offset'; page: number; perPage: number; offset: number; limit: number}
| {strategy: 'cursor'; cursor: string | undefined; limit: number};

Offset strategy:

Property Type Description
strategy 'offset' Active strategy identifier
page number Current page number (clamped to ≥ 1)
perPage number Items per page (clamped to ≤ maxPerPage)
offset number Computed: (page - 1) * perPage
limit number Same as perPage

Cursor strategy:

Property Type Description
strategy 'cursor' Active strategy identifier
cursor string | undefined Opaque cursor token from query; undefined on first page
limit number Items to fetch (clamped to ≤ maxLimit)

Full documentation →

Stored at acc.body by the body() middleware. Parsed request body with metadata.

interface BodyResult {
type: string;
charset: string;
encoding: string;
length: number | undefined;
received: number;
boundary: string | undefined;
raw: string;
parsed: unknown;
}
Property Type Description
type string Content-Type MIME type
charset string Character encoding (e.g. 'utf-8')
encoding string Content-Encoding (e.g. 'identity', 'gzip')
length number | undefined Content-Length header value; undefined for chunked
received number Actual bytes received
boundary string | undefined Multipart boundary; undefined for non-multipart
raw string Raw body string before parsing
parsed unknown | undefined Parsed body (JSON object, form data, etc.)

Full documentation →

Stored at acc.cookies by the cookie() middleware. Cookie jar with incoming cookies as own properties and methods for managing outgoing cookies.

interface CookieJar {
set(name: string, value: string, options?: Record<string, unknown>): void;
get(name: string): unknown;
clear(name: string): void;
toHeader(): string[];
readonly size: number;
readonly isJar: true;
[name: string]: unknown;
}

Incoming cookies from the Cookie request header are available as own properties on the jar (e.g. acc.cookies.sessionId). Outgoing cookies are managed via set(), get(), clear(), and serialized to Set-Cookie headers by toHeader().

Full documentation →

Stored at acc.log by the logger() middleware. Structured request metadata captured at pipeline entry.

interface LogEntry {
requestId: string;
timestamp: number;
ip: string;
method: string;
url: string;
httpVersion: string;
host: Readonly<{
hostname: string;
arch: string;
platform: string;
pid: number;
}>;
request: {
headers: Record<string, string | string[] | undefined>;
encrypted: boolean;
remoteAddress: string;
remotePort: number;
};
traceId?: string;
spanId?: string;
}

Full documentation →

Stored at acc.accepts by the accepts() middleware. Content negotiation results.

interface AcceptsResult {
type: string | false;
language: string | false;
charset: string | false;
encoding: string | false;
}
Property Type Description
type string | false Best matching content type, or false if unacceptable
language string | false Best matching language
charset string | false Best matching charset
encoding string | false Best matching encoding

Full documentation →

Stored at acc.prefer by the prefer() middleware. Parsed RFC 7240 Prefer header as a flat map.

interface PreferResult {
[preference: string]: string | true;
}

Preference names map to their value string, or true for bare tokens. Example: {return: 'minimal', 'respond-async': true}.

Full documentation →

Stored at acc.auth by the authorization() middleware. The authenticated identity returned by the matching strategy’s authorizer function.

type AuthorizationResult = Record<string, unknown>;

The shape is opaque — it is entirely determined by the user-provided authorizer function’s info return value. The type system represents this as Record<string, unknown>; consumers narrow via application-level type assertions or generics.

Common patterns:

// Bearer — authorizer returns user object
acc.auth // → { user: { id: "42", role: "admin" } }
// Basic — authorizer returns credentials metadata
acc.auth // → { username: "jane", permissions: ["read", "write"] }
// API Key — authorizer returns service identity
acc.auth // → { service: "billing", scopes: ["invoices:read"] }

Full documentation →

Stored at acc.trace by the tracing() middleware. OpenTelemetry span and context for the current request.

interface TracingResult {
span: import('@opentelemetry/api').Span;
tracer?: import('@opentelemetry/api').Tracer;
activeContext?: import('@opentelemetry/api').Context;
parentContext: import('@opentelemetry/api').Context;
traceId: string;
spanId: string;
}
Property Type Description
span Span Active span for the request pipeline
tracer Tracer | undefined Only populated when perStage: true
activeContext Context | undefined Only populated when perStage: true
parentContext Context Parent context for child span creation
traceId string W3C trace ID (hex string)
spanId string Span ID (hex string)

Full documentation →

Stored at acc.idempotency by the idempotency() middleware when the middleware participates. Absent (undefined) when the HTTP method is not applicable or the Idempotency-Key header is missing and not required — compose-with skips all merges on undefined/null returns.

type IdempotencyResult =
| {key: string; fingerprint: string; complete: (response: unknown) => boolean; discard: () => void}
| {replayed: true};
Variant When Shape
Active key First request with this key — store the response when done {key, fingerprint, complete(), discard()}
Replayed Subsequent request with a completed key — response was replayed {replayed: true}

Full documentation →

When using @centralping/ergo-router, the domain accumulator is seeded with route match data before the pipeline runs. These values are available to all middleware and the execute handler.

Route parameters extracted by the router’s path matching (powered by find-my-way). Available immediately — not produced by a middleware config object.

// Route: "/users/:id/posts/:postId"
// Request: GET /users/42/posts/7
acc.route.params // → { id: "42", postId: "7" }

Parameter values are always strings. Numeric conversion is the consumer’s responsibility.

The response accumulator collects HTTP response properties set by middleware. When any middleware sets statusCode, the pipeline breaks immediately. After pipeline completion (or break), send() reads the response accumulator to format the HTTP response.

interface ResponseAccumulator {
statusCode?: number;
headers?: [string, string][];
body?: unknown;
detail?: string;
retryAfter?: number;
instance?: string;
location?: string;
lastModified?: Date;
type?: string;
[key: string]: unknown;
}
Property Type Description
statusCode number HTTP status code — setting this breaks the pipeline
headers [string, string][] Response headers as [name, value] tuples (appended, not overwritten)
body unknown Response body (serialized by send())
detail string RFC 9457 Problem Details detail field
retryAfter number Retry-After header value in seconds
instance string RFC 9457 instance URI
location string Location header value (redirects, 201 Created)
lastModified Date Last-Modified header value
type string RFC 9457 type URI