Skip to content
Dashboard

Custom Class Serialization in Workflow SDK

, John Lindquist
import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from "@workflow/serde";
interface SerializedSandbox {
metadata: SandboxSnapshot;
routes: SandboxRouteData[];
}
export class Sandbox {
sandbox: SandboxSnapshot;
routes: SandboxRouteData[];
// Serialize a Sandbox instance to plain data
static [WORKFLOW_SERIALIZE](instance: Sandbox): SerializedSandbox {
return {
metadata: instance.sandbox,
routes: instance.routes,
};
}
// Deserialize a Sandbox from serialized data
static [WORKFLOW_DESERIALIZE](data: SerializedSandbox): Sandbox {
return new Sandbox({
sandbox: data.metadata,
routes: data.routes,
});
}
// If instance methods require Node.js APIs, they are marked with
// "use step" too allow them to be called from the workflow
async runCommand(
commandOrParams: string | RunCommandParams,
args?: string[],
opts?: { signal?: AbortSignal },
): Promise<Command | CommandFinished> {
"use step";
// ...
}
}

Example of how `@vercel/sandbox` implements workflow custom class serialization

workflow/code-runner.ts
export async function runCode(prompt: string) {
"use workflow";
// Sandbox.create() has "use step" built in, so it runs as a
// durable step. The returned Sandbox instance is automatically
// serialized via WORKFLOW_SERIALIZE when it crosses the step boundary.
const sandbox = await Sandbox.create({
resources: { vcpus: 1 },
timeout: 5 * 60 * 1000,
runtime: "node22",
});
// Each Sandbox instance method (writeFiles, runCommand, stop, etc.)
// also has "use step" built in, so every call below is its own
// durable step — and the sandbox object is automatically rehydrated
// via WORKFLOW_DESERIALIZE at each step boundary.
const code = 'console.log("Hello World")';
await sandbox.writeFiles([{ path: "script.js", content: code }]);
const finished = await sandbox.runCommand("node", ["script.js"]);
// ...
}

Example usage of the serialized `Sandbox` class within Workflow DevKit


Ready to deploy?