send
The terminal response writer in ergo’s two-accumulator model. Called once
by handler() (or ergo-router’s auto-wrap) after the pipeline
completes. Reads from both the response accumulator and domain
accumulator to serialize the HTTP response.
send is not placed inside the pipeline — it runs after the pipeline
via handler().
Pipeline stage: Post-pipeline (called by handler)
Import
Section titled “Import”import { send } from "@centralping/ergo";Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
prettify | boolean | false | Pretty-print JSON output |
vary | string[] | ['Accept'] | Vary header values to append |
etag | boolean | true | Generate ETags and evaluate conditional headers |
prefer | boolean | false | Read domainAcc.prefer for RFC 7240 return=minimal / return=representation. When true, appends Prefer to the Vary header |
paginate | boolean | false | Read domainAcc.paginate and responseAcc.paginate to auto-generate RFC 8288 Link headers and X-Total-Count for paginated responses |
envelope | boolean | function | false | Wrap 2xx Object bodies in a response envelope |
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 |
Envelope Values
Section titled “Envelope Values”| Value | Behavior |
|---|---|
false | No envelope (default) |
true | Built-in { id, status, data, count? } — id is read from the x-request-id response header |
function | Custom (body, ctx) => wrappedBody |
Accumulator Inputs
Section titled “Accumulator Inputs”Response accumulator: statusCode, body, headers, detail,
retryAfter, instance, location, lastModified, type,
paginate (pagination response metadata: total, nextCursor,
prevCursor)
Domain accumulator: cookies (cookie jar → Set-Cookie), prefer
(parsed Prefer header), paginate (parsed pagination parameters)
Body Handling (statusCode < 400)
Section titled “Body Handling (statusCode < 400)”| Body Type | Content-Type | Behavior |
|---|---|---|
null / undefined | — | Default status text (empty for 204/304) |
string | Auto-detected HTML or text | Written as-is |
Uint8Array | application/octet-stream | Written as binary |
Stream | — | Piped to response |
Object | application/json | JSON-serialized (respects prettify) |
Error Responses (statusCode ≥ 400)
Section titled “Error Responses (statusCode ≥ 400)”Error bodies are automatically formatted as
RFC 9457 Problem Details from
the response accumulator’s statusCode, detail, retryAfter,
instance, and any extension members.
Conditional Requests
Section titled “Conditional Requests”| Status | Condition |
|---|---|
| 304 Not Modified | If-None-Match weak-matches the ETag, or If-Modified-Since and resource not modified |
| 412 Precondition Failed | If-Match fails on unsafe methods, or If-Unmodified-Since fails |
| 204 No Content | Prefer: return=minimal on 2xx responses (200 → 204) |
Setting lastModified
Section titled “Setting lastModified”Return lastModified on the response accumulator to set the
Last-Modified header and enable date-based conditional request
evaluation. The value can be a Date or a date string — unparseable
values are silently ignored (no header set, no conditional evaluation).
import { handler, compose } from "@centralping/ergo";
const pipeline = compose( async (req, res, acc) => { const todo = await db.findById("42"); return { response: { statusCode: 200, body: todo, lastModified: todo.updatedAt, }, }; },);
const server = http.createServer(handler(pipeline));// GET /todos/42// → 200 OK// → Last-Modified: Thu, 05 Jun 2025 12:00:00 GMTrouter.get("/todos/:id", { execute: async (req, res, acc) => { const todo = await db.findById(acc.route.params.id); return { response: { statusCode: 200, body: todo, lastModified: todo.updatedAt, }, }; },});// GET /todos/42// → 200 OK// → Last-Modified: Thu, 05 Jun 2025 12:00:00 GMTWhen a client sends the Last-Modified value back in an
If-Modified-Since header, send() automatically returns
304 Not Modified with no body if the resource has not changed.
Dates are compared at second granularity.
GET /todos/42If-Modified-Since: Thu, 05 Jun 2025 12:00:00 GMT
→ 304 Not Modified (no body)Write Protection with If-Unmodified-Since
Section titled “Write Protection with If-Unmodified-Since”For unsafe methods (PUT, PATCH, DELETE), send() evaluates
If-Unmodified-Since as a write-protection guard. If the resource was
modified after the condition date, the request is rejected with
412 Precondition Failed.
import { handler, compose, body } from "@centralping/ergo";
const pipeline = compose( {fn: body(), setPath: "body"}, async (req, res, acc) => { const todo = await db.findAndUpdate("42", acc.body.parsed); return { response: { statusCode: 200, body: todo, lastModified: todo.updatedAt, }, }; },);
const server = http.createServer(handler(pipeline));// PUT /todos/42// If-Unmodified-Since: Thu, 05 Jun 2025 11:00:00 GMT// (resource modified at 12:00:00 — after the condition date)// → 412 Precondition Failedrouter.put("/todos/:id", { execute: async (req, res, acc) => { const todo = await db.findAndUpdate( acc.route.params.id, acc.body.parsed, ); return { response: { statusCode: 200, body: todo, lastModified: todo.updatedAt, }, }; },});// PUT /todos/42// If-Unmodified-Since: Thu, 05 Jun 2025 11:00:00 GMT// (resource modified at 12:00:00 — after the condition date)// → 412 Precondition Failedimport { handler, compose, send } from "@centralping/ergo";
const pipeline = compose( (req, res, acc) => ({ response: { statusCode: 200, body: { hello: "world" } }, }),);
const server = http.createServer( handler(pipeline, { etag: true, prettify: true }),);const router = createRouter({ defaults: { send: { etag: true, prefer: true }, },});RFC References
Section titled “RFC References”- RFC 9110 — HTTP Semantics
- RFC 9457 — Problem Details for HTTP APIs
- RFC 7240 — Prefer Header for HTTP
Related Recipes
Section titled “Related Recipes”- Pagination — End-to-end offset and cursor pagination with auto-generated Link headers
API Reference
Section titled “API Reference”See the auto-generated send API docs.