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 middleware registers its result at a fixed path on the domain accumulator via {fn, setPath} config objects. The table below maps every pipeline-builder key to its accumulator path and typed interface.

Config KeysetPath (acc.*)Type InterfacePipeline Stage
tracingtraceTracingResultNegotiation
loggerlogLogEntryNegotiation
rateLimitrateLimitNegotiation
acceptsacceptsAcceptsResultNegotiation
preconditionRequiredpreconditionNegotiation
cookiecookiesCookieJarNegotiation
urlurlUrlResultNegotiation
paginatepaginatePaginateResultNegotiation
jsonApiQueryjsonApiQueryNegotiation
preferpreferPreferResultNegotiation
securityHeaderssecurityNegotiation
cacheControlcacheNegotiation
csrfcsrfAuthorization
authorizationauthAuthorizationResultAuthorization
bodybodyBodyResultValidation
idempotencyidempotencyIdempotencyResultValidation
validatevalidationValidation

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

interface UrlResult {
query: Record<string, string | string[]>;
pathname: string | undefined;
search: string | undefined;
}
PropertyTypeDescription
pathnamestring | undefinedURL path before the query string
searchstring | undefinedRaw query string including leading ?; undefined when absent
queryRecord<string, string | string[]>Parsed query parameters (null-prototype object); multi-value keys become arrays

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:

PropertyTypeDescription
strategy'offset'Active strategy identifier
pagenumberCurrent page number (clamped to ≥ 1)
perPagenumberItems per page (clamped to ≤ maxPerPage)
offsetnumberComputed: (page - 1) * perPage
limitnumberSame as perPage

Cursor strategy:

PropertyTypeDescription
strategy'cursor'Active strategy identifier
cursorstring | undefinedOpaque cursor token from query; undefined on first page
limitnumberItems 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;
}
PropertyTypeDescription
typestringContent-Type MIME type
charsetstringCharacter encoding (e.g. 'utf-8')
encodingstringContent-Encoding (e.g. 'identity', 'gzip')
lengthnumber | undefinedContent-Length header value; undefined for chunked
receivednumberActual bytes received
boundarystring | undefinedMultipart boundary; undefined for non-multipart
rawstringRaw body string before parsing
parsedunknownParsed 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;
}
PropertyTypeDescription
typestring | falseBest matching content type, or false if unacceptable
languagestring | falseBest matching language
charsetstring | falseBest matching charset
encodingstring | falseBest 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;
}
PropertyTypeDescription
spanSpanActive span for the request pipeline
tracerTracer | undefinedOnly populated when perStage: true
activeContextContext | undefinedOnly populated when perStage: true
parentContextContextParent context for child span creation
traceIdstringW3C trace ID (hex string)
spanIdstringSpan ID (hex string)

Full documentation →

Stored at acc.idempotency by the idempotency() middleware. Idempotency-Key processing result — a discriminated union with three variants.

type IdempotencyResult =
| Record<string, never>
| { key: string; fingerprint: string; complete: (response: unknown) => void; discard: () => void }
| { replayed: true };
VariantWhenShape
Empty object {}No Idempotency-Key header present (and not required)Record<string, never>
Active keyFirst request with this key — store the response when done{ key, fingerprint, complete(), discard() }
ReplayedSubsequent 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;
}
PropertyTypeDescription
statusCodenumberHTTP status code — setting this breaks the pipeline
headers[string, string][]Response headers as [name, value] tuples (appended, not overwritten)
bodyunknownResponse body (serialized by send())
detailstringRFC 9457 Problem Details detail field
retryAfternumberRetry-After header value in seconds
instancestringRFC 9457 instance URI
locationstringLocation header value (redirects, 201 Created)
lastModifiedDateLast-Modified header value
typestringRFC 9457 type URI