Cookbook
Complete, runnable recipes for common tasks in Cruft: a JSON REST API, a SQLite-backed store, sandboxing a plugin in a compartment, a parallel worker pool, password hashing, signing and verifying tokens, fetching JSON, running tests, and sealing dependencies against I/O.
Task-oriented recipes: complete, runnable solutions to common tasks. Every recipe here is complete and runnable; the output shown is real. Reference pages are linked for depth.
A JSON REST API
cruft:serve gives you a fetch-shaped server; route on method + path.
// api.mjs — cruft api.mjs
import { serve } from "cruft:serve";
const users = [{ id: 1, name: "ada" }];
serve({ port: 8080, handler: async (req) => {
const url = new URL(req.url);
if (req.method === "GET" && url.pathname === "/users")
return new Response(JSON.stringify(users),
{ headers: { "content-type": "application/json" } });
if (req.method === "POST" && url.pathname === "/users") {
const u = { id: users.length + 1, ...(await req.json()) };
users.push(u);
return { status: 201, headers: { "content-type": "application/json" },
body: JSON.stringify(u) };
}
return { status: 404, body: "not found" };
}});
$ curl -s localhost:8080/users
[{"id":1,"name":"ada"}]
$ curl -s -X POST -d '{"name":"grace"}' localhost:8080/users
{"id":2,"name":"grace"}
More: cruft:serve. For a per-route sandboxed edge, see the capability gateway.
A database-backed store
SQLite is built in, no install, no daemon. It writes the real sqlite3 file format, so app.db opens in any SQLite tool.
import { Database } from "bun:sqlite";
const db = new Database("app.db"); // or ":memory:"
db.exec("CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY, task TEXT, done INTEGER DEFAULT 0)");
db.prepare("INSERT INTO todos (task) VALUES (?)").run("write docs");
db.prepare("UPDATE todos SET done = 1 WHERE id = ?").run(1);
db.query("SELECT * FROM todos ORDER BY id").all();
// [{ id: 1, task: "write docs", done: 1 }, …]
For a typed, boundary-validated data layer over SQLite or Postgres, use the ORM; the engine details are in the SQL stack.
Sandbox a plugin (or model output)
Run code you don't fully trust in a compartment: it gets only the API you hand it, and a deadline it can't escape.
function runPlugin(src, api) {
const c = new Compartment({ globals: api, timeout_ms: 100 });
try { return { ok: true, value: c.evaluate(src) }; }
catch (e) { return { ok: false, error: e.message }; }
}
const api = { greet: (name) => "hi " + name };
runPlugin(`greet("ada")`, api); // { ok: true, value: "hi ada" }
runPlugin(`while (true) {}`, api); // { ok: false, error: "…exceeded its 100 ms timeout" }
runPlugin(`typeof require`, api); // { ok: true, value: "undefined" } — no ambient authority
The plugin can't reach require, fetch, or the filesystem, only greet. See Compartments; for running LLM-authored code with an audit log, see the agent sandbox.
A parallel worker pool
Offload CPU work to worker-hosted compartments; request returns the result as a Promise. (node:worker_threads also works, round-tripping messages to a file-backed worker; a compartment worker adds capability isolation on top. An inline eval:-mode Worker is not supported yet, point it at a file.)
const worker = new Compartment({
worker: true,
// request(x) delivers the message as { data: x }, so unwrap msg.data:
onMessageSource: `(msg) => msg.data.reduce((a, b) => a + b, 0)`,
});
// fan out, collect results
const chunks = [[1, 2], [3, 4], [5, 6]];
const sums = await Promise.all(chunks.map((c) => worker.request(c)));
// [3, 7, 11]
send is the fire-and-forget variant; request is request/reply. Details and the shared-memory lane: Workers.
Hash and verify a password
scrypt for the hash, timingSafeEqual for the comparison (never === on secrets).
import crypto from "node:crypto";
function hash(pw) {
const salt = crypto.randomBytes(16);
const dk = crypto.scryptSync(pw, salt, 32);
return salt.toString("hex") + ":" + dk.toString("hex");
}
function verify(pw, stored) {
const [salt, h] = stored.split(":");
const dk = crypto.scryptSync(pw, Buffer.from(salt, "hex"), 32);
return crypto.timingSafeEqual(dk, Buffer.from(h, "hex"));
}
const stored = hash("s3cret");
verify("s3cret", stored); // true
verify("guess", stored); // false
Sign and verify a token
HMAC over a payload, with WebCrypto. (For asymmetric, ECDSA-P256 and Ed25519 work the same way, see the crypto reference.)
const enc = new TextEncoder();
const key = await crypto.subtle.importKey(
"raw", enc.encode("server-secret"),
{ name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"]);
const payload = JSON.stringify({ user: "ada", exp: 9999999999 });
const sig = await crypto.subtle.sign("HMAC", key, enc.encode(payload));
const token = btoa(payload) + "." + Buffer.from(sig).toString("base64url");
// verify
const [p, s] = token.split(".");
const ok = await crypto.subtle.verify(
"HMAC", key, Buffer.from(s, "base64url"), enc.encode(atob(p)));
// ok === true; JSON.parse(atob(p)).user === "ada"
Full algorithm matrix: WebCrypto reference. The crypto rejects tampered and forged inputs; see the security model for the entropy and constant-time boundaries.
Fetch and transform JSON
fetch is a global, real HTTP/1.1 over http and https.
const repo = await fetch("https://api.github.com/repos/nodejs/node",
{ headers: { "user-agent": "cruft" } })
.then((r) => r.json());
console.log(repo.stargazers_count); // a real number
fetch follows redirects, honors an AbortController signal (aborting with an AbortError), and the TLS client validates the certificate chain, hostname, and expiry, so ordinary HTTPS to third parties works.
Run tests
node:test + cruft --test.
// math.test.mjs
import test from "node:test";
import assert from "node:assert";
test("adds", () => assert.strictEqual(2 + 2, 4));
$ cruft --test # discovers *.test.* files, exits non-zero on failure
✔ adds (0.53ms)
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 2.4
Details and the compatibility-exception list: test runner.
Harden a deployment
Deny your dependencies I/O with one flag, no code change:
$ cruft --sealed-deps app.mjs
Your code runs normally; anything under node_modules that tries to touch the filesystem or network throws. Learn a program's real footprint first with cruft --audit app.mjs. This is the production-ready supply-chain control; see the security model and the deployment guide. (Full --sealed also enforces, gating network, filesystem, and directory enumeration with a stdio grant; --sealed-deps is the simplest starting point.)