Node http
permdock/node wraps the Fetch kernel for raw node:http handlers (IncomingMessage and ServerResponse) and for any framework not covered by a dedicated adapter.
Purpose
Some services run on node:http directly, on a framework without a PermDock adapter (Koa, Polka, h3), or on a custom server. permdock/node converts IncomingMessage / ServerResponse to the Fetch Request / Response pair the server kernel expects, and exposes the kernel's permdock, protect and problem in a callback shape. It is also the conversion layer the Express and Nest adapters reuse. Runtimes that already expose Fetch handlers (Bun, Deno, Workers, srvx) should use permdock/server directly.
API
import { createServer } from "node:http";
import { createPermDock } from "permdock/node";
import { policy } from "./policy";
import { permissions } from "./permissions";
const { permdock, protect, send } = createPermDock(policy, {
subject: (req) => userFromCookie(req.headers.cookie),
});
createServer(async (req, res) => {
const instance = await permdock(req);
if (req.method === "DELETE" && req.url?.startsWith("/posts/")) {
const guard = await protect(permissions.post.delete, () =>
loadPost(idFrom(req.url)),
)(req);
if (!guard.ok) return send(res, guard.response);
await deletePost(guard.data);
res.statusCode = 204;
return res.end();
}
res.statusCode = 200;
res.end(
JSON.stringify(instance.filter(permissions.post.read, await listPosts())),
);
}).listen(3000);| Export | Role |
|---|---|
permdock(req) | Converts the request once (headers, method, URL; body is streamed lazily), resolves the subject, builds the instance, memoises it in a WeakMap keyed by req. |
protect(permission, loadData?, options?) | Returns (req) => Promise<Guard>; same result shape as the kernel (ok, permdock, decision, data or response). The tenant option ((req) => ...) is resolved for each call. |
send(res, response) | Writes a Fetch Response to a ServerResponse (status, headers, body). Used for Problem Details and for the decision endpoint. |
permdockHandler() | (req, res) => Promise<void> implementing the AuthZEN-shaped decision endpoint for mounting under any path. |
toRequest(req) / fromResponse(res, response) | Exported converters so other adapters and custom frameworks can reuse them. |
Request lifecycle
permdock/node follows the shared adapter contract through the server kernel. What differs on raw node:http:
permdock(req)memoises onreq, so later calls with the same request return the same instance.- A PermDock error thrown by
asserthas no framework hook to catch it: wrap the handler andsendthe result ofproblemFromError(error)frompermdock/serverwhen it returns aResponse. - The conversion is lazy. Headers, method and URL are copied when
permdock(req)is first called; an unread body is exposed as a pull stream (highWaterMark: 0) on the FetchRequestand only read when a loader or the decision endpoint consumes it, so a body parser that runs later still sees every byte. A parsedreq.body(Express, Nest) is reused instead of re-reading the stream. A missingHostheader falls back tolocalhostand does not throw. permdockHandler()validates decision-endpoint bodies against the AuthZENevaluationsschema.
Denials are the kernel's Problem Details responses, written through send.
Example app
No dedicated example. Covered by unit tests using node:http on an ephemeral port and by the Express and Nest examples, which reuse the same converters.
tests/integration/src/http/node.test.ts runs the testHttpAdapter scenarios on node:http with a URLPattern router, a formData() upload behind protect and problemFromError for assert.
Related standards
Last updated on
NestJS
permdock/nest provides a module, a guard and a Protect decorator so Nest controllers get a request-scoped PermDock on the Express or Fastify platform.
Terminal (your own CLI)
permdock/terminal puts permission checks inside command-line tools you build with commander, citty, oclif, yargs, clack or Ink, with verified subjects from device flow, keychain, env or CI tokens, sysexits exit codes and Problem Details on --json.