With Durable Objects
Overview
Section titled “Overview”Each Durable Object has its own WASM instance with its own WebAssembly.Memory. zerobuf wraps that memory, giving you structured zero-copy access to WASM data.
Pattern
Section titled “Pattern”import { DurableObject } from "cloudflare:workers";import { zerobuf, type ZeroBuf } from "zerobuf";
export class FragmentDO extends DurableObject<Env> { private buf!: ZeroBuf; private wasmExports!: WasmExports;
private async ensureInitialized() { if (this.buf) return; const instance = await WebAssembly.instantiate(wasmModule); this.wasmExports = instance.exports as WasmExports; this.buf = zerobuf( this.wasmExports.memory, this.wasmExports.__heap_base.value, ); }
// Public method = RPC endpoint async scan(request: ScanRequest): Promise<ScanResult> { await this.ensureInitialized();
// Save arena state before per-request allocations const checkpoint = this.buf.save();
// WASM computes, writes result at a known pointer this.wasmExports.executeScan(request.ptr); const resultPtr = this.wasmExports.getResultPtr();
// Wrap as accessor object — lazy, zero copy const result = this.buf.wrapObject(resultPtr);
// Convert to plain JS before returning over RPC // (RPC serializes, so we need a plain JS object) const response = result.toJS() as ScanResult;
// Restore arena — frees all per-request allocations this.buf.restore(checkpoint);
return response; }}Key Points
Section titled “Key Points”One zerobuf per DO instance. Each DO has its own WASM memory. Create the zerobuf instance once in ensureInitialized().
toJS before RPC. zerobuf Proxy objects reference WASM memory via absolute pointers. When you return data over DO RPC, call .toJS() to convert into a plain JS object that can be serialized.
Arena lifecycle. WASM memory persists as long as the DO is alive (until hibernation evicts it). Use buf.save()/buf.restore() for per-request cleanup — save before each request, extract results with .toJS(), then restore to free all per-request allocations. Without this, the arena leaks memory on every request.
No race conditions. JS and WASM on the same DO never run concurrently — each RPC call runs to completion before the next one starts.
When to Use zerobuf vs Raw Buffers
Section titled “When to Use zerobuf vs Raw Buffers”| Data type | Use |
|---|---|
| Query results, metadata, configs | zerobuf — structured, lazy access |
| Typed columns (Float64Array, Int64) | Raw typed buffers — tighter packing, SIMD-friendly |
| Strings, nested objects | zerobuf — handles encoding, growth |
| Hot compute paths | Raw buffers for input, zerobuf for output |