UNPKG

workflow

Version:

Workflow DevKit - Build durable, resilient, and observable workflows

165 lines (120 loc) 5.74 kB
--- title: Serialization description: Understand how workflow data is serialized and persisted across suspensions and resumptions. type: conceptual summary: Learn which types can be passed between workflow and step functions. prerequisites: - /docs/foundations/workflows-and-steps related: - /docs/errors/serialization-failed --- All function arguments and return values passed between workflow and step functions must be serializable. Workflow DevKit uses a custom serialization system built on top of [devalue](https://github.com/sveltejs/devalue). This system supports standard JSON types, as well as a few additional popular Web API types. <Callout type="info"> The serialization system ensures that all data persists correctly across workflow suspensions and resumptions, enabling durable execution. </Callout> ## Supported Serializable Types The following types can be serialized and passed through workflow functions: **Standard JSON Types:** - `string` - `number` - `boolean` - `null` - Arrays of serializable values - Objects with string keys and serializable values **Extended Types:** - `undefined` - `bigint` - `ArrayBuffer` - `BigInt64Array`, `BigUint64Array` - `Date` - `Float32Array`, `Float64Array` - `Int8Array`, `Int16Array`, `Int32Array` - `Map<Serializable, Serializable>` - `RegExp` - `Set<Serializable>` - `URL` - `URLSearchParams` - `Uint8Array`, `Uint8ClampedArray`, `Uint16Array`, `Uint32Array` **Notable:** <Callout type="info"> These types have special handling and are explained in detail in the sections below. </Callout> - `Headers` - `Request` - `Response` - `ReadableStream<Serializable>` - `WritableStream<Serializable>` ## Streaming `ReadableStream` and `WritableStream` are supported as serializable types with special handling. These streams can be passed between workflow and step functions while maintaining their streaming capabilities. For complete information about using streams in workflows, including patterns for AI streaming, file processing, and progress updates, see the [Streaming Guide](/docs/foundations/streaming). ## Request & Response The Web API [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) APIs are supported by the serialization system, and can be passed around between workflow and step functions similarly to other data types. As a convenience, these two APIs are treated slightly differently when used within a workflow function: calling the `text()` / `json()` / `arrayBuffer()` instance methods is automatically treated as a step function invocation. This allows you to consume the body directly in the workflow context while maintaining proper serialization and caching. For example, consider how receiving a webhook request provides the entire `Request` instance into the workflow context. You may consume the body of that request directly in the workflow, which will be cached as a step result for future resumptions of the workflow: ```typescript title="workflows/webhook.ts" lineNumbers import { createWebhook } from "workflow"; export async function handleWebhookWorkflow() { "use workflow"; const webhook = createWebhook(); const request = await webhook; // The body of the request will only be consumed once // [!code highlight] const body = await request.json(); // [!code highlight] // … } ``` ### Using `fetch` in Workflows Because `Request` and `Response` are serializable, Workflow DevKit provides a `fetch` function that can be used directly in workflow functions: ```typescript title="workflows/api-call.ts" lineNumbers import { fetch } from "workflow"; // [!code highlight] export async function apiWorkflow() { "use workflow"; // fetch can be called directly in workflows // [!code highlight] const response = await fetch("https://api.example.com/data"); // [!code highlight] const data = await response.json(); return data; } ``` The implementation is straightforward - `fetch` from workflow is a step function that wraps the standard `fetch`: ```typescript title="Implementation" lineNumbers export async function fetch(...args: Parameters<typeof globalThis.fetch>) { "use step"; return globalThis.fetch(...args); } ``` This allows you to make HTTP requests directly in workflow functions while maintaining deterministic replay behavior through automatic caching. ## Pass-by-Value Semantics **Parameters are passed by value, not by reference.** Steps receive deserialized copies of data. Mutations inside a step won't affect the original in the workflow. **Incorrect:** ```typescript title="workflows/incorrect-mutation.ts" lineNumbers export async function updateUserWorkflow(userId: string) { "use workflow"; let user = { id: userId, name: "John", email: "john@example.com" }; await updateUserStep(user); // user.email is still "john@example.com" // [!code highlight] console.log(user.email); // [!code highlight] } async function updateUserStep(user: { id: string; name: string; email: string }) { "use step"; user.email = "newemail@example.com"; // Changes are lost // [!code highlight] } ``` **Correct - return the modified data:** ```typescript title="workflows/correct-mutation.ts" lineNumbers export async function updateUserWorkflow(userId: string) { "use workflow"; let user = { id: userId, name: "John", email: "john@example.com" }; user = await updateUserStep(user); // Reassign the return value // [!code highlight] console.log(user.email); // "newemail@example.com" } async function updateUserStep(user: { id: string; name: string; email: string }) { "use step"; user.email = "newemail@example.com"; return user; // [!code highlight] } ```