Render

Server-side HTML / JSON / XML response helpers. Each helper picks the right Content-Type, sets a sensible Cache-Control + security header default, and ends the response in one call — replacing the five-line writeHead / stringify / Content-Length / end ritual that every route handler otherwise reimplements.

Module-level helpers (json / text / htmlString / redirect) work without a template engine. create({ engine }) wraps a b.template.create instance and returns the same helpers plus html(res, viewName, data?) for engine-rendered pages. Operators who never render server-side HTML import only the module-level helpers and skip the engine wiring entirely.

All helpers fall through silently when res.writableEnded === true, so a late Promise rejection after res.end can't corrupt the wire with a half-written second body. The default Cache-Control is private, no-cache, must-revalidate — overridable via opts.headers["Cache-Control"] for CDN-cacheable responses.

b.render.json(res, body, opts) #

stable0.1.0
{
  status:   200,                  // numeric HTTP status (200/201/202/4xx/5xx)
  headers:  {},                   // merged over defaults; later wins
  replacer: function|string[],    // JSON.stringify replacer (BigInt/Date/redaction)
}

JSON-stringifies body and writes it to res with Content-Type application/json; charset=utf-8, an explicit Content-Length, and the dynamic-response Cache-Control. Status defaults to 200; any custom headers in opts.headers merge over the defaults so operators can pin a different Cache-Control or add CORS headers without losing Content-Type. Returns undefined — the response is fully written by the time the call returns.

opts.replacer is forwarded to JSON.stringify (ECMA-262 §25.5.2, the second argument) so handlers can serialize values that have no native JSON form — BigInt (which otherwise throws), Date in a custom shape, Map / Set, or a redaction filter over secret- shaped keys — without pre-walking the body. Accepts the same function or property-name array JSON.stringify does; a non- function / non-array value is a config typo and throws.

b.render.json(res, { ok: true, id: 42 }, { status: 201 });
// → response: 201, application/json, body `{"ok":true,"id":42}`

b.render.json(res, { total: 9007199254740993n }, {
  replacer: function (k, v) { return typeof v === "bigint" ? v.toString() : v; },
});
// → body `{"total":"9007199254740993"}`

b.render.text(res, body, opts) #

stable0.1.0
{
  status:  200,
  headers: {},
  charset: "utf-8",
}

Coerces body to a string and writes it as text/plain with the supplied charset (default utf-8). null / undefined body becomes the empty string rather than the literal text "null" — a common gotcha when forwarding a value-or-nothing handler result.

b.render.text(res, "OK");
// → 200, Content-Type "text/plain; charset=utf-8", body "OK"

b.render.htmlString(res, htmlBody, opts) #

stable0.1.0
{
  status:  200,
  headers: {},
  charset: "utf-8",
}

Writes a pre-rendered HTML string with Content-Type: text/html; charset=. Use when an HTML body is already in hand — for engine-bound view rendering, prefer b.render.create({ engine }) and the returned html(res, viewName, data) helper which threads res.locals (CSP nonce, request id, current user) into the view.

b.render.htmlString(res, "

Hi

"); // → 200, text/html; charset=utf-8, body "

Hi

"

b.render.redirect(res, location, opts) #

stable0.1.0
{
  status:  302,   // 301 / 302 / 303 / 307 / 308
  headers: {},
}

Sends a 3xx response with the given Location header and an empty body. Throws when location is empty or when opts.status falls outside the 300–399 range. Default status is 302; pass 301 / 303 / 307 / 308 for the other RFC 7231 / 7538 redirect semantics. For untrusted user-supplied destinations, validate first via b.safeRedirect before passing the result here.

b.render.redirect(res, "/login", { status: 303 });
// → 303, Location "/login", empty body

b.render.create(opts) #

stable0.1.0
{
  engine: ,   // a template engine instance from b.template.create({ viewsDir })
}

Binds a template engine to a renderer and returns the module-level helpers (json / text / htmlString / redirect) plus html(res, viewName, data?, opts?). The html helper auto-merges res.locals into the template data so request-scoped values (CSP nonce, request id, current user) thread through every render without per-route plumbing. Operator-supplied data keys take precedence over locals — explicit beats implicit. Throws when opts.engine.render is not a function.

var engine = b.template.create({ viewsDir: "/srv/views" });
var r      = b.render.create({ engine: engine });
r.html(res, "home", { user: "ada" });
// → 200, text/html; charset=utf-8, body = engine.render("home", merged-locals)

Last updated 2026-08-08T16:39:49.652Z by seeder.