capnwasm dynamic schema demo
Runtime schema, no generated reader/builder. This is the path for tenant-uploaded schemas, admin tools, schema explorers, or anything where the shape arrives after the app ships.
Live result
The browser defines a User schema as plain JS data, builds Cap'n Proto bytes,
decodes only selected fields with pick(["id", "name", "active"]), then POSTs
the bytes to /api/echo. The Worker decodes and re-encodes the same wire shape.
Loading wasm runtimeā¦
The whole demo
import { load } from "capnwasm/browser";
import { defineSchema, buildDynamic, openDynamic } from "capnwasm/dynamic";
const User = defineSchema({
id: { kind: "uint64", offset: 0 },
name: { kind: "text", slot: 0 },
email: { kind: "text", slot: 1 },
joinedAtMs: { kind: "uint64", offset: 8 },
active: { kind: "bool", bitOffset: 128 },
avatar: { kind: "data", slot: 2 },
}, { dataWords: 3, ptrWords: 3 });
const cpp = await load();
const bytes = buildDynamic(cpp, User)
.fromObject({ id: 42n, name: "Dynamic Ada", active: true, avatar })
.finalize();
const picked = openDynamic(cpp, User, bytes).pick(["id", "name", "active"]);
const echoed = await fetch("/api/echo", { method: "POST", body: bytes }).then(r => r.arrayBuffer());
const roundTrip = openDynamic(cpp, User, new Uint8Array(echoed)).toObject();
When to use dynamic vs codegen
| dynamic | codegen | |
|---|---|---|
| Schema known at build time | works | preferred |
| Schema arrives at runtime | only option | not applicable |
| Read arbitrary field subset | pick(names) | works |
| Hot typed path | slower per field | faster |