workflow 5.0.0-beta.5 → 5.0.0-beta.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -23
- package/dist/api-workflow.d.ts +1 -1
- package/dist/api-workflow.d.ts.map +1 -1
- package/dist/api-workflow.js +1 -1
- package/dist/api.d.ts +3 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +5 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/internal/builtins.d.ts +20 -3
- package/dist/internal/builtins.d.ts.map +1 -1
- package/dist/internal/builtins.js +68 -4
- package/dist/internal/errors.d.ts +1 -1
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/errors.js +2 -2
- package/dist/nest-builder.d.ts +2 -0
- package/dist/nest-builder.d.ts.map +1 -0
- package/dist/nest-builder.js +2 -0
- package/dist/nest-vercel-builder.d.ts +2 -0
- package/dist/nest-vercel-builder.d.ts.map +1 -0
- package/dist/nest-vercel-builder.js +2 -0
- package/dist/observability.d.ts +1 -1
- package/dist/observability.js +2 -2
- package/dist/runtime.d.ts +2 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +4 -1
- package/docs/ai/chat-session-modeling.mdx +176 -422
- package/docs/ai/defining-tools.mdx +6 -7
- package/docs/ai/human-in-the-loop.mdx +11 -11
- package/docs/ai/index.mdx +71 -75
- package/docs/ai/message-queueing.mdx +71 -110
- package/docs/ai/meta.json +1 -0
- package/docs/ai/resumable-streams.mdx +40 -28
- package/docs/ai/sleep-and-delays.mdx +10 -10
- package/docs/ai/streaming-updates-from-tools.mdx +6 -6
- package/docs/api-reference/index.mdx +25 -1
- package/docs/api-reference/meta.json +8 -0
- package/docs/api-reference/vitest/index.mdx +9 -15
- package/docs/api-reference/workflow/create-hook.mdx +89 -10
- package/docs/api-reference/workflow/create-webhook.mdx +16 -15
- package/docs/api-reference/workflow/define-hook.mdx +35 -33
- package/docs/api-reference/workflow/fatal-error.mdx +30 -8
- package/docs/api-reference/workflow/fetch.mdx +14 -10
- package/docs/api-reference/workflow/get-step-metadata.mdx +2 -2
- package/docs/api-reference/workflow/get-workflow-metadata.mdx +3 -3
- package/docs/api-reference/workflow/get-writable.mdx +7 -7
- package/docs/api-reference/workflow/index.mdx +4 -1
- package/docs/api-reference/workflow/retryable-error.mdx +1 -1
- package/docs/api-reference/workflow/set-attributes.mdx +61 -0
- package/docs/api-reference/workflow/sleep.mdx +4 -4
- package/docs/api-reference/workflow-ai/durable-agent.mdx +63 -101
- package/docs/api-reference/workflow-ai/index.mdx +5 -5
- package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +67 -24
- package/docs/api-reference/workflow-api/get-hook-by-token.mdx +26 -12
- package/docs/api-reference/workflow-api/get-run.mdx +43 -8
- package/docs/api-reference/workflow-api/index.mdx +6 -10
- package/docs/api-reference/workflow-api/resume-hook.mdx +73 -12
- package/docs/api-reference/workflow-api/resume-webhook.mdx +11 -9
- package/docs/api-reference/workflow-api/start.mdx +60 -13
- package/docs/api-reference/workflow-astro/index.mdx +18 -0
- package/docs/api-reference/workflow-astro/meta.json +4 -0
- package/docs/api-reference/workflow-astro/workflow.mdx +45 -0
- package/docs/api-reference/workflow-errors/entity-conflict-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/hook-conflict-error.mdx +60 -0
- package/docs/api-reference/workflow-errors/hook-not-found-error.mdx +8 -8
- package/docs/api-reference/workflow-errors/index.mdx +88 -0
- package/docs/api-reference/workflow-errors/meta.json +6 -0
- package/docs/api-reference/workflow-errors/precondition-failed-error.mdx +68 -0
- package/docs/api-reference/workflow-errors/run-expired-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/run-not-supported-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/step-not-registered-error.mdx +5 -5
- package/docs/api-reference/workflow-errors/throttle-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/too-early-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/workflow-error.mdx +52 -0
- package/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx +5 -6
- package/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx +6 -6
- package/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +5 -5
- package/docs/api-reference/workflow-errors/workflow-run-not-completed-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/workflow-runtime-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/workflow-world-error.mdx +8 -8
- package/docs/api-reference/workflow-globals.mdx +14 -10
- package/docs/api-reference/workflow-nest/configure-workflow-controller.mdx +33 -0
- package/docs/api-reference/workflow-nest/index.mdx +31 -0
- package/docs/api-reference/workflow-nest/meta.json +9 -0
- package/docs/api-reference/workflow-nest/nest-local-builder.mdx +64 -0
- package/docs/api-reference/workflow-nest/workflow-controller.mdx +40 -0
- package/docs/api-reference/workflow-nest/workflow-module.mdx +74 -0
- package/docs/api-reference/workflow-next/with-workflow.mdx +39 -17
- package/docs/api-reference/workflow-nitro/index.mdx +60 -0
- package/docs/api-reference/workflow-nuxt/index.mdx +48 -0
- package/docs/api-reference/workflow-observability/hydrate-data.mdx +35 -0
- package/docs/api-reference/workflow-observability/hydrate-resource-io.mdx +62 -0
- package/docs/api-reference/workflow-observability/index.mdx +62 -0
- package/docs/api-reference/workflow-observability/meta.json +11 -0
- package/docs/api-reference/workflow-observability/observability-revivers.mdx +50 -0
- package/docs/api-reference/workflow-observability/parse-class-name.mdx +41 -0
- package/docs/api-reference/workflow-observability/parse-step-name.mdx +40 -0
- package/docs/api-reference/workflow-observability/parse-workflow-name.mdx +55 -0
- package/docs/api-reference/workflow-runtime/create-world.mdx +39 -0
- package/docs/api-reference/workflow-runtime/get-world-handlers.mdx +44 -0
- package/docs/api-reference/{workflow-api → workflow-runtime}/get-world.mdx +11 -14
- package/docs/api-reference/workflow-runtime/health-check.mdx +50 -0
- package/docs/api-reference/workflow-runtime/index.mdx +41 -0
- package/docs/api-reference/workflow-runtime/meta.json +12 -0
- package/docs/api-reference/workflow-runtime/set-world.mdx +51 -0
- package/docs/api-reference/workflow-runtime/workflow-entrypoint.mdx +43 -0
- package/docs/api-reference/workflow-runtime/world/analytics.mdx +315 -0
- package/docs/api-reference/workflow-runtime/world/index.mdx +60 -0
- package/docs/api-reference/workflow-runtime/world/meta.json +4 -0
- package/docs/api-reference/workflow-runtime/world/queue.mdx +88 -0
- package/docs/api-reference/{workflow-api → workflow-runtime}/world/storage.mdx +98 -34
- package/docs/api-reference/{workflow-api → workflow-runtime}/world/streams.mdx +8 -8
- package/docs/api-reference/workflow-serde/index.mdx +1 -2
- package/docs/api-reference/workflow-serde/workflow-deserialize.mdx +3 -4
- package/docs/api-reference/workflow-serde/workflow-serialize.mdx +8 -8
- package/docs/api-reference/workflow-sveltekit/index.mdx +18 -0
- package/docs/api-reference/workflow-sveltekit/meta.json +4 -0
- package/docs/api-reference/workflow-sveltekit/workflow-plugin.mdx +42 -0
- package/docs/api-reference/workflow-vite/index.mdx +18 -0
- package/docs/api-reference/workflow-vite/meta.json +4 -0
- package/docs/api-reference/workflow-vite/workflow.mdx +48 -0
- package/docs/changelog/attributes-mvp.mdx +380 -0
- package/docs/changelog/batched-event-writes.mdx +79 -0
- package/docs/changelog/eager-processing.mdx +110 -436
- package/docs/changelog/index.mdx +4 -2
- package/docs/changelog/lazy-event-creation.md +127 -0
- package/docs/changelog/lazy-hook-resume.mdx +78 -0
- package/docs/changelog/meta.json +11 -1
- package/docs/changelog/resilient-resume.mdx +32 -0
- package/docs/changelog/resilient-start.mdx +33 -285
- package/docs/changelog/step-message-ownership.mdx +360 -0
- package/docs/changelog/turbo-mode.md +87 -0
- package/docs/comparisons/index.mdx +66 -0
- package/docs/comparisons/meta.json +11 -0
- package/docs/comparisons/workflow-sdk-vs-aws-agentcore.mdx +55 -0
- package/docs/comparisons/workflow-sdk-vs-aws-step-functions.mdx +111 -0
- package/docs/comparisons/workflow-sdk-vs-cloudflare-workflows.mdx +71 -0
- package/docs/comparisons/workflow-sdk-vs-inngest.mdx +102 -0
- package/docs/comparisons/workflow-sdk-vs-temporal.mdx +123 -0
- package/docs/comparisons/workflow-sdk-vs-trigger-dev.mdx +104 -0
- package/docs/configuration/build-and-diagnostics.mdx +70 -0
- package/docs/configuration/cli-and-web-ui.mdx +241 -0
- package/docs/configuration/framework-options.mdx +165 -0
- package/docs/configuration/index.mdx +32 -0
- package/docs/configuration/meta.json +12 -0
- package/docs/configuration/runtime-tuning.mdx +381 -0
- package/docs/configuration/worlds.mdx +313 -0
- package/docs/cookbook/advanced/child-workflows.mdx +211 -264
- package/docs/cookbook/advanced/meta.json +6 -1
- package/docs/cookbook/advanced/publishing-libraries.mdx +65 -56
- package/docs/cookbook/advanced/serializable-steps.mdx +48 -68
- package/docs/cookbook/advanced/upgrading-workflows.mdx +199 -0
- package/docs/cookbook/agent-patterns/agent-cancellation.mdx +78 -60
- package/docs/cookbook/agent-patterns/durable-agent.mdx +23 -131
- package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +180 -195
- package/docs/cookbook/common-patterns/batching.mdx +18 -14
- package/docs/cookbook/common-patterns/idempotency.mdx +41 -53
- package/docs/cookbook/common-patterns/rate-limiting.mdx +8 -4
- package/docs/cookbook/common-patterns/saga.mdx +23 -19
- package/docs/cookbook/common-patterns/scheduling.mdx +34 -22
- package/docs/cookbook/common-patterns/sequential-and-parallel.mdx +29 -25
- package/docs/cookbook/common-patterns/timeouts.mdx +26 -21
- package/docs/cookbook/common-patterns/webhooks.mdx +10 -6
- package/docs/cookbook/common-patterns/workflow-composition.mdx +30 -27
- package/docs/cookbook/index.mdx +22 -21
- package/docs/cookbook/integrations/ai-sdk.mdx +86 -48
- package/docs/cookbook/integrations/chat-sdk.mdx +50 -33
- package/docs/cookbook/integrations/sandbox.mdx +62 -45
- package/docs/deploying.mdx +95 -0
- package/docs/errors/abort-signal-timeout-in-workflow.mdx +16 -12
- package/docs/errors/corrupted-event-log.mdx +39 -18
- package/docs/errors/deployment-mismatch.mdx +71 -0
- package/docs/errors/fetch-in-workflow.mdx +15 -14
- package/docs/errors/hook-conflict.mdx +69 -13
- package/docs/errors/index.mdx +2 -36
- package/docs/errors/node-js-module-in-workflow.mdx +9 -5
- package/docs/errors/replay-divergence.mdx +27 -0
- package/docs/errors/run-expired.mdx +85 -0
- package/docs/errors/runtime-decryption-failed.mdx +77 -0
- package/docs/errors/serialization-failed.mdx +44 -12
- package/docs/errors/start-invalid-workflow-function.mdx +9 -5
- package/docs/errors/step-executed-multiple-times.mdx +23 -0
- package/docs/errors/step-not-registered.mdx +6 -6
- package/docs/errors/timeout-in-workflow.mdx +12 -8
- package/docs/errors/webhook-invalid-respond-with-value.mdx +18 -18
- package/docs/errors/webhook-response-not-sent.mdx +20 -16
- package/docs/errors/workflow-not-registered.mdx +5 -5
- package/docs/foundations/cancellation.mdx +31 -32
- package/docs/foundations/errors-and-retries.mdx +42 -11
- package/docs/foundations/hooks.mdx +64 -35
- package/docs/foundations/idempotency.mdx +244 -12
- package/docs/foundations/index.mdx +1 -23
- package/docs/foundations/meta.json +2 -1
- package/docs/foundations/serialization.mdx +21 -22
- package/docs/foundations/starting-workflows.mdx +106 -30
- package/docs/foundations/streaming.mdx +108 -60
- package/docs/foundations/versioning.mdx +263 -0
- package/docs/foundations/workflows-and-steps.mdx +9 -9
- package/docs/getting-started/astro.mdx +22 -18
- package/docs/getting-started/express.mdx +15 -11
- package/docs/getting-started/fastify.mdx +15 -11
- package/docs/getting-started/hono.mdx +15 -11
- package/docs/getting-started/index.mdx +10 -3
- package/docs/getting-started/meta.json +3 -1
- package/docs/getting-started/nestjs.mdx +87 -20
- package/docs/getting-started/next.mdx +22 -16
- package/docs/getting-started/nitro.mdx +22 -18
- package/docs/getting-started/nuxt.mdx +15 -11
- package/docs/getting-started/python.mdx +190 -41
- package/docs/getting-started/react-router/index.mdx +33 -0
- package/docs/getting-started/react-router/meta.json +5 -0
- package/docs/getting-started/react-router/v7.mdx +237 -0
- package/docs/getting-started/react-router/v8.mdx +232 -0
- package/docs/getting-started/sveltekit.mdx +20 -16
- package/docs/getting-started/tanstack-start.mdx +17 -13
- package/docs/getting-started/vite.mdx +15 -11
- package/docs/how-it-works/cancellation.mdx +63 -63
- package/docs/how-it-works/code-transform.mdx +83 -67
- package/docs/how-it-works/encryption.mdx +30 -26
- package/docs/how-it-works/event-sourcing.mdx +125 -34
- package/docs/how-it-works/framework-integrations.mdx +96 -337
- package/docs/how-it-works/understanding-directives.mdx +22 -22
- package/docs/internal/index.mdx +6 -4
- package/docs/internal/meta.json +6 -1
- package/docs/internal/nitro-native-build.mdx +38 -0
- package/docs/internal/nitro-web-ui.mdx +24 -0
- package/docs/internal/serializable-abort-controller.mdx +7 -7
- package/docs/meta.json +3 -2
- package/docs/observability/attributes.mdx +134 -0
- package/docs/observability/index.mdx +32 -10
- package/docs/observability/meta.json +1 -1
- package/docs/observability/retention.mdx +95 -0
- package/docs/observability/tracing.mdx +124 -0
- package/docs/testing/index.mdx +36 -36
- package/docs/testing/server-based.mdx +10 -10
- package/docs/whats-new.mdx +186 -0
- package/package.json +17 -14
- package/docs/api-reference/workflow-api/world/index.mdx +0 -58
- package/docs/api-reference/workflow-api/world/meta.json +0 -4
- package/docs/api-reference/workflow-api/world/observability.mdx +0 -164
- package/docs/api-reference/workflow-api/world/queue.mdx +0 -86
- package/docs/deploying/building-a-world.mdx +0 -251
- package/docs/deploying/index.mdx +0 -95
- package/docs/deploying/meta.json +0 -4
- package/docs/deploying/world/local-world.mdx +0 -84
- package/docs/deploying/world/meta.json +0 -4
- package/docs/deploying/world/postgres-world.mdx +0 -224
- package/docs/deploying/world/vercel-world.mdx +0 -179
- package/docs/migration-guides/index.mdx +0 -34
- package/docs/migration-guides/meta.json +0 -9
- package/docs/migration-guides/migrating-from-aws-step-functions.mdx +0 -363
- package/docs/migration-guides/migrating-from-inngest.mdx +0 -314
- package/docs/migration-guides/migrating-from-temporal.mdx +0 -318
- package/docs/migration-guides/migrating-from-trigger-dev.mdx +0 -337
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Versioning
|
|
3
|
+
description: Understand how workflow runs are pinned to deployments, how to recover runs after a fix, and how to opt in to newer code explicitly.
|
|
4
|
+
type: guide
|
|
5
|
+
summary: Keep in-flight runs stable by default, then choose explicit upgrade boundaries when you need them.
|
|
6
|
+
prerequisites:
|
|
7
|
+
- /docs/foundations/starting-workflows
|
|
8
|
+
related:
|
|
9
|
+
- /docs/api-reference/workflow-api/start
|
|
10
|
+
- /docs/foundations/cancellation
|
|
11
|
+
- /cookbook/common-patterns/workflow-composition
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
Workflow runs are pinned to the deployment that starts them. When a run begins, Workflow SDK records the deployment for that run and continues executing the run on that same copy of your code.
|
|
15
|
+
|
|
16
|
+
That default is intentional. Durable workflows can pause for minutes, days, or months. If the code underneath a paused run changed every time you deployed, an in-flight run could resume into a different function body, different step names, or different input types than the ones it started with. That can make type safety fragile and can break long-running work in hard-to-debug ways.
|
|
17
|
+
|
|
18
|
+
With Workflow SDK, you can keep shipping. New runs use new deployments, while existing runs keep the version they already understand.
|
|
19
|
+
|
|
20
|
+
## Default behavior
|
|
21
|
+
|
|
22
|
+
Start a workflow normally:
|
|
23
|
+
|
|
24
|
+
```typescript title="app/api/orders/route.ts" lineNumbers
|
|
25
|
+
import { start } from "workflow/api";
|
|
26
|
+
import { fulfillOrder } from "@/workflows/fulfill-order";
|
|
27
|
+
|
|
28
|
+
export async function POST(request: Request) {
|
|
29
|
+
const { orderId } = await request.json();
|
|
30
|
+
|
|
31
|
+
const run = await start(fulfillOrder, [orderId]); // [!code highlight]
|
|
32
|
+
|
|
33
|
+
return Response.json({ runId: run.runId });
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The run is tied to the deployment that handled this request. If you deploy a new version while the workflow is [sleeping](/docs/api-reference/workflow/sleep), [waiting on a hook](/docs/foundations/hooks), [retrying a step](/docs/foundations/errors-and-retries), or processing later queue messages, that existing run still resumes on the original deployment.
|
|
38
|
+
|
|
39
|
+
```typescript title="workflows/fulfill-order.ts" lineNumbers
|
|
40
|
+
import { sleep } from "workflow";
|
|
41
|
+
|
|
42
|
+
export async function fulfillOrder(orderId: string) {
|
|
43
|
+
"use workflow";
|
|
44
|
+
|
|
45
|
+
await reserveInventory(orderId);
|
|
46
|
+
await sleep("2d");
|
|
47
|
+
await chargeCustomer(orderId);
|
|
48
|
+
await shipOrder(orderId);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function reserveInventory(orderId: string) {
|
|
52
|
+
"use step";
|
|
53
|
+
// ...
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function chargeCustomer(orderId: string) {
|
|
57
|
+
"use step";
|
|
58
|
+
// ...
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function shipOrder(orderId: string) {
|
|
62
|
+
"use step";
|
|
63
|
+
// ...
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
If you deploy a change to `chargeCustomer()` while a run is in the two-day sleep, the existing run does not suddenly resume into the new implementation. It continues on the deployment it started on. The next order starts on the latest deployment and uses the new code from the beginning.
|
|
68
|
+
|
|
69
|
+
## Fixing in-flight runs
|
|
70
|
+
|
|
71
|
+
Sometimes you deploy because the old code had a bug. The safest fix is usually explicit:
|
|
72
|
+
|
|
73
|
+
1. Deploy the fixed code.
|
|
74
|
+
2. Find the affected runs in [observability](/docs/observability) or with the CLI.
|
|
75
|
+
3. Cancel the old runs if they are still running.
|
|
76
|
+
4. Rerun them on the latest deployment with the same inputs.
|
|
77
|
+
|
|
78
|
+
This keeps the version boundary visible. The old run ends as canceled or failed, and the replacement run starts fresh on the fixed deployment. This is a good fit for one-off, ad-hoc upgrades where you explicitly opt in to moving affected runs onto a new version.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Inspect affected runs and copy the exact workflowName value.
|
|
82
|
+
npx workflow inspect runs \
|
|
83
|
+
--backend vercel \
|
|
84
|
+
--status running
|
|
85
|
+
|
|
86
|
+
# Cancel one run.
|
|
87
|
+
npx workflow cancel <run-id> \
|
|
88
|
+
--backend vercel
|
|
89
|
+
|
|
90
|
+
# Or bulk-cancel matching running runs.
|
|
91
|
+
npx workflow cancel \
|
|
92
|
+
--status running \
|
|
93
|
+
--workflowName "workflow//./workflows/fulfill-order//fulfillOrder" \
|
|
94
|
+
--backend vercel
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The `--workflowName` filter expects the generated workflow ID, not only the exported function's short name. Use the `workflowName` value from `workflow inspect runs`, and use [`parseWorkflowName()`](/docs/api-reference/workflow-observability/parse-workflow-name) when you need display-friendly names.
|
|
98
|
+
|
|
99
|
+
In the [observability UI](/docs/observability), use **Rerun on latest** to enqueue the workflow again with the same inputs against the latest deployment.
|
|
100
|
+
|
|
101
|
+
If you are writing your own recovery route, call `start()` with the same arguments and `deploymentId: "latest"`:
|
|
102
|
+
|
|
103
|
+
```typescript title="app/api/orders/rerun/route.ts" lineNumbers
|
|
104
|
+
import { start } from "workflow/api";
|
|
105
|
+
import { fulfillOrder } from "@/workflows/fulfill-order";
|
|
106
|
+
|
|
107
|
+
export async function POST(request: Request) {
|
|
108
|
+
const { orderId } = await request.json();
|
|
109
|
+
|
|
110
|
+
const run = await start(fulfillOrder, [orderId], {
|
|
111
|
+
deploymentId: "latest", // [!code highlight]
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return Response.json({ runId: run.runId });
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
<Callout type="warn">
|
|
119
|
+
`deploymentId: "latest"` is currently a Vercel-specific feature. Other Worlds may implement this option differently to match their own deployment runtimes, and the World spec may rename it from `deploymentId` to `version` in a future SDK version. On Vercel, `"latest"` resolves to the most recent deployment matching your current environment. Because the caller and target deployment can be different, keep the [workflow function name and file path](/docs/errors/workflow-not-registered), arguments, and return value backward-compatible across the deployments you plan to bridge.
|
|
120
|
+
</Callout>
|
|
121
|
+
|
|
122
|
+
## Self upgrading workflows
|
|
123
|
+
|
|
124
|
+
Some workflows are expected to run for long periods. Scheduled loops, recurring jobs, agents, and chat sessions often should not stay on one deployment forever.
|
|
125
|
+
|
|
126
|
+
Model those as a sequence of runs. Each run does a bounded piece of work, then starts the next run on the latest deployment and exits. This is similar to `continueAsNew` in other durable execution systems, but Workflow SDK uses [explicit recursion through `start()`](/cookbook/common-patterns/workflow-composition).
|
|
127
|
+
|
|
128
|
+
```typescript title="workflows/daily-digest.ts" lineNumbers
|
|
129
|
+
import { sleep } from "workflow";
|
|
130
|
+
import { start } from "workflow/api";
|
|
131
|
+
|
|
132
|
+
type DigestState = {
|
|
133
|
+
userId: string;
|
|
134
|
+
lastSentAt?: string;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export async function dailyDigest(state: DigestState) {
|
|
138
|
+
"use workflow";
|
|
139
|
+
|
|
140
|
+
const sentAt = await sendDigest(state.userId);
|
|
141
|
+
await sleep("1d");
|
|
142
|
+
|
|
143
|
+
const run = await start(
|
|
144
|
+
dailyDigest,
|
|
145
|
+
[{ ...state, lastSentAt: sentAt }],
|
|
146
|
+
{
|
|
147
|
+
deploymentId: "latest", // [!code highlight]
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
return { continuedAs: run.runId };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function sendDigest(userId: string) {
|
|
155
|
+
"use step";
|
|
156
|
+
// ...
|
|
157
|
+
return new Date().toISOString();
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
This pattern gives every run a clear lifecycle:
|
|
162
|
+
|
|
163
|
+
- The current run stays on its original deployment.
|
|
164
|
+
- The next run starts on the latest deployment.
|
|
165
|
+
- The [serialized `state`](/docs/foundations/serialization) is the migration boundary between versions.
|
|
166
|
+
- Observability can link parent and child runs when a workflow starts another run.
|
|
167
|
+
|
|
168
|
+
## Carrying context forward
|
|
169
|
+
|
|
170
|
+
Anything that is [serializable by Workflow SDK](/docs/foundations/serialization) can be passed from one run to the next as an argument. That includes plain state objects, `ReadableStream`, `WritableStream`, `AbortSignal`, and other supported serialized values.
|
|
171
|
+
|
|
172
|
+
For example, a long export can register its [output stream](/docs/foundations/streaming) once, write progress from each run, and pass the same stream plus updated state into the next run:
|
|
173
|
+
|
|
174
|
+
```typescript title="workflows/export-report.ts" lineNumbers
|
|
175
|
+
import { getWritable } from "workflow";
|
|
176
|
+
import { start } from "workflow/api";
|
|
177
|
+
|
|
178
|
+
type ExportState = {
|
|
179
|
+
exportId: string;
|
|
180
|
+
page: number;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export async function exportReport(
|
|
184
|
+
state: ExportState,
|
|
185
|
+
progress?: WritableStream<string>
|
|
186
|
+
) {
|
|
187
|
+
"use workflow";
|
|
188
|
+
|
|
189
|
+
// Register the stream once. Continuation runs receive this same stream
|
|
190
|
+
// as an argument and keep writing to it.
|
|
191
|
+
const stream =
|
|
192
|
+
progress !== undefined ? progress : getWritable<string>();
|
|
193
|
+
|
|
194
|
+
const hasMore = await exportPage(state, stream);
|
|
195
|
+
|
|
196
|
+
if (!hasMore) {
|
|
197
|
+
await writeProgress(stream, { type: "done", totalPages: state.page });
|
|
198
|
+
return { totalPages: state.page };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const run = await start(exportReport, [
|
|
202
|
+
{ ...state, page: state.page + 1 },
|
|
203
|
+
stream,
|
|
204
|
+
], {
|
|
205
|
+
deploymentId: "latest", // [!code highlight]
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
return { continuedAs: run.runId };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function exportPage(
|
|
212
|
+
state: ExportState,
|
|
213
|
+
stream: WritableStream<string>
|
|
214
|
+
) {
|
|
215
|
+
"use step";
|
|
216
|
+
|
|
217
|
+
// Do work for this version boundary.
|
|
218
|
+
const hasMore = state.page < 10;
|
|
219
|
+
const writer = stream.getWriter();
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
await writer.write(
|
|
223
|
+
JSON.stringify({ type: "page", page: state.page }) + "\n"
|
|
224
|
+
);
|
|
225
|
+
return hasMore;
|
|
226
|
+
} finally {
|
|
227
|
+
writer.releaseLock();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function writeProgress(
|
|
232
|
+
stream: WritableStream<string>,
|
|
233
|
+
event: { type: "done"; totalPages: number }
|
|
234
|
+
) {
|
|
235
|
+
"use step";
|
|
236
|
+
|
|
237
|
+
const writer = stream.getWriter();
|
|
238
|
+
try {
|
|
239
|
+
await writer.write(JSON.stringify(event) + "\n");
|
|
240
|
+
} finally {
|
|
241
|
+
writer.releaseLock();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
```typescript title="app/api/export/route.ts" lineNumbers
|
|
247
|
+
import { start } from "workflow/api";
|
|
248
|
+
import { exportReport } from "@/workflows/export-report";
|
|
249
|
+
|
|
250
|
+
export async function POST(request: Request) {
|
|
251
|
+
const { exportId } = await request.json();
|
|
252
|
+
|
|
253
|
+
const run = await start(exportReport, [{ exportId, page: 1 }]);
|
|
254
|
+
|
|
255
|
+
// Linked continuation runs keep writing to the stream registered by
|
|
256
|
+
// the parent run, because that stream is passed forward as an argument.
|
|
257
|
+
return new Response(run.readable, {
|
|
258
|
+
headers: { "Content-Type": "application/jsonl" },
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Each run still has one clear version boundary: the current run stays on its original deployment, the next run starts on the latest deployment, and only the explicit state and stream handle are carried forward.
|
|
@@ -14,18 +14,18 @@ import { File, Folder, Files } from "fumadocs-ui/components/files";
|
|
|
14
14
|
|
|
15
15
|
Workflows (a.k.a. *durable functions*) are a programming model for building long-running, stateful application logic that can maintain its execution state across restarts, failures, or user events. Unlike traditional serverless functions that lose all state when they terminate, workflows persist their progress and can resume exactly where they left off.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Workflows let you model complex multi-step processes in code. To do this, we introduce two fundamental entities:
|
|
18
18
|
|
|
19
19
|
1. **Workflow Functions**: Functions that orchestrate/organize steps
|
|
20
20
|
2. **Step Functions**: Functions that carry out the actual work
|
|
21
21
|
|
|
22
|
-
## Workflow
|
|
22
|
+
## Workflow functions
|
|
23
23
|
|
|
24
24
|
*Directive: `"use workflow"`*
|
|
25
25
|
|
|
26
26
|
Workflow functions define the entrypoint of a workflow and organize how step functions are called. This type of function does not have access to the Node.js runtime, and usable `npm` packages are limited.
|
|
27
27
|
|
|
28
|
-
Although this may seem limiting initially, this feature is
|
|
28
|
+
Although this may seem limiting initially, this feature is required to suspend and accurately resume workflow execution.
|
|
29
29
|
|
|
30
30
|
It helps to think of the workflow function less like a full JavaScript runtime and more like "stitching together" various steps using conditionals, loops, try/catch handlers, `Promise.all`, and other language primitives.
|
|
31
31
|
|
|
@@ -51,7 +51,7 @@ Determinism in the workflow is required to resume the workflow from a suspension
|
|
|
51
51
|
|
|
52
52
|
The sandboxed environment that workflows run in already ensures determinism. For instance, `Math.random` and `Date` constructors are fixed in workflow runs, so you are safe to use them, and the framework ensures that the values don't change across replays.
|
|
53
53
|
|
|
54
|
-
## Step
|
|
54
|
+
## Step functions
|
|
55
55
|
|
|
56
56
|
*Directive: `"use step"`*
|
|
57
57
|
|
|
@@ -115,10 +115,10 @@ export async function POST() {
|
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
<Callout type="info">
|
|
118
|
-
|
|
118
|
+
Calling a step function outside a workflow function provides neither retry semantics nor observability. Additionally, certain workflow-specific functions like [`getStepMetadata()`](/docs/api-reference/workflow/get-step-metadata) will throw an error when used inside a step that's called outside a workflow.
|
|
119
119
|
</Callout>
|
|
120
120
|
|
|
121
|
-
### Suspension and
|
|
121
|
+
### Suspension and resumption
|
|
122
122
|
|
|
123
123
|
Workflow functions have the ability to automatically suspend while they wait on asynchronous work. While suspended, the workflow's state is stored via the [event log](/docs/how-it-works/event-sourcing) and no compute resources are used until the workflow resumes execution.
|
|
124
124
|
|
|
@@ -126,7 +126,7 @@ Workflow functions have the ability to automatically suspend while they wait on
|
|
|
126
126
|
|
|
127
127
|
There are multiple ways a workflow can suspend:
|
|
128
128
|
|
|
129
|
-
- Waiting on a step function: the workflow yields while the step runs in the step
|
|
129
|
+
- Waiting on a step function: the workflow yields while the step body runs. The step usually executes inline in the same invocation; when the invocation's inline budget is exhausted or its timeout approaches, the step is handed to the queue and the workflow resumes in a later invocation.
|
|
130
130
|
- Using `sleep()` to pause for some fixed duration.
|
|
131
131
|
- Awaiting on a promise returned by [`createWebhook()`](/docs/api-reference/workflow/create-webhook), which resumes the workflow when an external system passes data into the workflow.
|
|
132
132
|
|
|
@@ -150,9 +150,9 @@ export async function documentReviewProcess(userId: string) {
|
|
|
150
150
|
}
|
|
151
151
|
```
|
|
152
152
|
|
|
153
|
-
## Writing
|
|
153
|
+
## Writing workflows
|
|
154
154
|
|
|
155
|
-
### Basic
|
|
155
|
+
### Basic structure
|
|
156
156
|
|
|
157
157
|
The simplest workflow consists of a workflow function and one or more step functions.
|
|
158
158
|
|
|
@@ -9,16 +9,20 @@ related:
|
|
|
9
9
|
- /docs/foundations/workflows-and-steps
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
<CopyPrompt
|
|
13
|
+
text="In this Astro app, run `npm i workflow`. In `astro.config.mjs`, import `workflow` from `workflow/astro` and add `integrations: [workflow()]`. Add the TypeScript plugin `{ "name": "workflow" }` to `tsconfig.json` if TypeScript is used. Create `src/workflows/user-signup.ts` exporting `handleUserSignup(email)` with `"use workflow"`, `sleep` from `workflow`, and `"use step"` helpers. Add `src/pages/api/signup.ts` exporting `POST: APIRoute` that reads `{ email }`, calls `start(handleUserSignup, [email])` from `workflow/api`, returns `Response.json`, and sets `prerender = false`. Run `npm run dev`, call `curl -X POST --json '{"email":"hello@example.com"}' http://localhost:4321/api/signup`, and inspect with `npx workflow inspect runs`."
|
|
14
|
+
/>
|
|
15
|
+
|
|
16
|
+
Set up your first durable workflow in an Astro app and learn the core Workflow SDK concepts.
|
|
13
17
|
|
|
14
18
|
---
|
|
15
19
|
|
|
16
20
|
<Steps>
|
|
17
21
|
|
|
18
22
|
<Step>
|
|
19
|
-
## Create
|
|
23
|
+
## Create your Astro project
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
Create an Astro project in a new directory named `my-workflow-app`:
|
|
22
26
|
|
|
23
27
|
```bash
|
|
24
28
|
npm create astro@latest my-workflow-app -- --template minimal --install --yes
|
|
@@ -55,16 +59,16 @@ export default defineConfig({
|
|
|
55
59
|
|
|
56
60
|
| Option | Type | Default | Description |
|
|
57
61
|
| --- | --- | --- | --- |
|
|
58
|
-
| `sourcemap` | `boolean \| 'inline' \| 'linked' \| 'external' \| 'both'` | `'inline'` | Controls source maps on generated workflow bundles. Accepts the same values as esbuild's `sourcemap` option.
|
|
62
|
+
| `sourcemap` | `boolean \| 'inline' \| 'linked' \| 'external' \| 'both'` | `'inline'` (dev) / `false` (prod) | Controls source maps on generated workflow bundles. Accepts the same values as esbuild's `sourcemap` option. Defaults to `'inline'` in development and `false` in production (smaller function bundles, which helps stay under the Vercel 250 MB function size limit). Set it explicitly, or use the `WORKFLOW_SOURCEMAP` environment variable, to override in either environment. |
|
|
59
63
|
|
|
60
64
|
<Accordion type="single" collapsible>
|
|
61
65
|
<AccordionItem value="typescript-intellisense" className="[&_h3]:my-0">
|
|
62
66
|
<AccordionTrigger className="text-sm">
|
|
63
|
-
###
|
|
67
|
+
### Set up IntelliSense for TypeScript (optional)
|
|
64
68
|
</AccordionTrigger>
|
|
65
69
|
<AccordionContent className="[&_p]:my-2">
|
|
66
70
|
|
|
67
|
-
To enable helpful hints in your IDE,
|
|
71
|
+
To enable helpful hints in your IDE, set up the workflow plugin in `tsconfig.json`:
|
|
68
72
|
|
|
69
73
|
```json title="tsconfig.json" lineNumbers
|
|
70
74
|
{
|
|
@@ -87,7 +91,7 @@ To enable helpful hints in your IDE, setup the workflow plugin in `tsconfig.json
|
|
|
87
91
|
|
|
88
92
|
<Step>
|
|
89
93
|
|
|
90
|
-
## Create
|
|
94
|
+
## Create your first workflow
|
|
91
95
|
|
|
92
96
|
Create a new file for our first workflow:
|
|
93
97
|
|
|
@@ -108,14 +112,14 @@ export async function handleUserSignup(email: string) {
|
|
|
108
112
|
|
|
109
113
|
```
|
|
110
114
|
|
|
111
|
-
We'll fill in those functions next, but
|
|
115
|
+
We'll fill in those functions next, but first review this code:
|
|
112
116
|
|
|
113
117
|
* We define a **workflow** function with the directive `"use workflow"`. Think of the workflow function as the _orchestrator_ of individual **steps**.
|
|
114
118
|
* The Workflow SDK's `sleep` function allows us to suspend execution of the workflow without using up any resources. A sleep can be a few seconds, hours, days, or even months long.
|
|
115
119
|
|
|
116
|
-
## Create
|
|
120
|
+
## Create your workflow steps
|
|
117
121
|
|
|
118
|
-
|
|
122
|
+
Define the missing functions.
|
|
119
123
|
|
|
120
124
|
```typescript title="src/workflows/user-signup.ts" lineNumbers
|
|
121
125
|
import { FatalError } from "workflow"
|
|
@@ -156,7 +160,7 @@ async function sendOnboardingEmail(user: { id: string; email: string}) {
|
|
|
156
160
|
|
|
157
161
|
Taking a look at this code:
|
|
158
162
|
|
|
159
|
-
* Business logic lives inside **steps**. When a
|
|
163
|
+
* Business logic lives inside **steps**. When a workflow invokes a step, the workflow suspends while the step runs with full Node.js access. The combined handler may execute it inline; queued retries and continuations return through the same flow route.
|
|
160
164
|
* If a step throws an error, like in `sendWelcomeEmail`, the step will automatically be retried until it succeeds (or hits the step's max retry count).
|
|
161
165
|
* Steps can throw a `FatalError` if an error is intentional and should not be retried.
|
|
162
166
|
|
|
@@ -168,7 +172,7 @@ We'll dive deeper into workflows, steps, and other ways to suspend or handle eve
|
|
|
168
172
|
|
|
169
173
|
<Step>
|
|
170
174
|
|
|
171
|
-
## Create
|
|
175
|
+
## Create your route handler
|
|
172
176
|
|
|
173
177
|
To invoke your new workflow, we'll have to add your workflow to a `POST` API route handler, `src/pages/api/signup.ts` with the following code:
|
|
174
178
|
|
|
@@ -200,7 +204,7 @@ Workflows can be triggered from API routes or any server-side code.
|
|
|
200
204
|
|
|
201
205
|
</Steps>
|
|
202
206
|
|
|
203
|
-
## Run in
|
|
207
|
+
## Run in development
|
|
204
208
|
|
|
205
209
|
To start your development server, run the following command in your terminal in the Vite root directory:
|
|
206
210
|
|
|
@@ -227,9 +231,9 @@ npx workflow inspect runs
|
|
|
227
231
|
|
|
228
232
|
---
|
|
229
233
|
|
|
230
|
-
## Deploying to
|
|
234
|
+
## Deploying to production
|
|
231
235
|
|
|
232
|
-
Workflow SDK apps currently work best when deployed to [Vercel](https://vercel.com/home) and
|
|
236
|
+
Workflow SDK apps currently work best when deployed to [Vercel](https://vercel.com/home) and need no special configuration.
|
|
233
237
|
|
|
234
238
|
<FluidComputeCallout />
|
|
235
239
|
|
|
@@ -247,8 +251,8 @@ Additionally, check the [Deploying](/docs/deploying) section to learn how your w
|
|
|
247
251
|
|
|
248
252
|
If you see this error:
|
|
249
253
|
|
|
250
|
-
```
|
|
251
|
-
'start' received an invalid workflow function. Ensure the Workflow
|
|
254
|
+
```text
|
|
255
|
+
'start' received an invalid workflow function. Ensure the Workflow SDK is configured correctly and the function includes a 'use workflow' directive.
|
|
252
256
|
```
|
|
253
257
|
|
|
254
258
|
Check both of these first:
|
|
@@ -258,7 +262,7 @@ Check both of these first:
|
|
|
258
262
|
|
|
259
263
|
See [start-invalid-workflow-function](/docs/errors/start-invalid-workflow-function) for full examples and fixes.
|
|
260
264
|
|
|
261
|
-
## Next
|
|
265
|
+
## Next steps
|
|
262
266
|
|
|
263
267
|
* Learn more about the [Foundations](/docs/foundations).
|
|
264
268
|
* Check [Errors](/docs/errors) if you encounter issues.
|
|
@@ -9,6 +9,10 @@ related:
|
|
|
9
9
|
- /docs/foundations/workflows-and-steps
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
+
<CopyPrompt
|
|
13
|
+
text="In this Express app, run `npm i workflow express nitro rollup` and, if using TypeScript, `npm i -D @types/express`. Create `nitro.config.ts` with `modules: ["workflow/nitro"]`, `vercel: { entryFormat: "node" }`, and `routes: { "/**": { handler: "./src/index.ts", format: "node" } }`. Add package scripts `dev: "nitro dev"` and `build: "nitro build"`. Create `workflows/user-signup.ts` with `"use workflow"`, `sleep`, and `"use step"` helpers. Add `src/index.ts` with Express JSON middleware, POST `/api/signup`, and `start(handleUserSignup, [email])` from `workflow/api`. Run `npm run dev`, call `curl -X POST --json '{"email":"hello@example.com"}' http://localhost:3000/api/signup`, and inspect with `npx workflow web` or `npx workflow inspect runs`."
|
|
14
|
+
/>
|
|
15
|
+
|
|
12
16
|
This guide will walk through setting up your first workflow in an Express app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
|
|
13
17
|
|
|
14
18
|
---
|
|
@@ -16,7 +20,7 @@ This guide will walk through setting up your first workflow in an Express app. A
|
|
|
16
20
|
<Steps>
|
|
17
21
|
|
|
18
22
|
<Step>
|
|
19
|
-
## Create
|
|
23
|
+
## Create your Express project
|
|
20
24
|
|
|
21
25
|
Start by creating a new Express project.
|
|
22
26
|
|
|
@@ -113,7 +117,7 @@ To use the Nitro builder, update your `package.json` to include the following sc
|
|
|
113
117
|
|
|
114
118
|
<Step>
|
|
115
119
|
|
|
116
|
-
## Create
|
|
120
|
+
## Create your first workflow
|
|
117
121
|
|
|
118
122
|
Create a new file for our first workflow:
|
|
119
123
|
|
|
@@ -133,14 +137,14 @@ export async function handleUserSignup(email: string) {
|
|
|
133
137
|
}
|
|
134
138
|
```
|
|
135
139
|
|
|
136
|
-
We'll fill in those functions next
|
|
140
|
+
We'll fill in those functions next. The current code does the following:
|
|
137
141
|
|
|
138
142
|
- We define a **workflow** function with the directive `"use workflow"`. Think of the workflow function as the _orchestrator_ of individual **steps**.
|
|
139
143
|
- The Workflow SDK's `sleep` function allows us to suspend execution of the workflow without using up any resources. A sleep can be a few seconds, hours, days, or even months long.
|
|
140
144
|
|
|
141
|
-
## Create
|
|
145
|
+
## Create your workflow steps
|
|
142
146
|
|
|
143
|
-
|
|
147
|
+
Define the missing functions.
|
|
144
148
|
|
|
145
149
|
```typescript title="workflows/user-signup.ts" lineNumbers
|
|
146
150
|
import { FatalError } from "workflow";
|
|
@@ -181,7 +185,7 @@ async function sendOnboardingEmail(user: { id: string; email: string }) {
|
|
|
181
185
|
|
|
182
186
|
Taking a look at this code:
|
|
183
187
|
|
|
184
|
-
- Business logic lives inside **steps**. When a
|
|
188
|
+
- Business logic lives inside **steps**. When a workflow invokes a step, the workflow suspends while the step runs with full Node.js access. The combined handler may execute it inline; queued retries and continuations return through the same flow route.
|
|
185
189
|
- If a step throws an error, like in `sendWelcomeEmail`, the step will automatically be retried until it succeeds (or hits the step's max retry count).
|
|
186
190
|
- Steps can throw a `FatalError` if an error is intentional and should not be retried.
|
|
187
191
|
|
|
@@ -194,7 +198,7 @@ Taking a look at this code:
|
|
|
194
198
|
|
|
195
199
|
<Step>
|
|
196
200
|
|
|
197
|
-
## Create
|
|
201
|
+
## Create your route handler
|
|
198
202
|
|
|
199
203
|
To invoke your new workflow, we'll create both the Express app and a new API route handler at `src/index.ts` with the following code:
|
|
200
204
|
|
|
@@ -256,7 +260,7 @@ npx workflow inspect runs
|
|
|
256
260
|
|
|
257
261
|
## Deploying to production
|
|
258
262
|
|
|
259
|
-
Workflow SDK apps currently work best when deployed to [Vercel](https://vercel.com/home) and
|
|
263
|
+
Workflow SDK apps currently work best when deployed to [Vercel](https://vercel.com/home) and need no special configuration.
|
|
260
264
|
|
|
261
265
|
<FluidComputeCallout />
|
|
262
266
|
|
|
@@ -268,8 +272,8 @@ Check the [Deploying](/docs/deploying) section to learn how your workflows can b
|
|
|
268
272
|
|
|
269
273
|
If you see this error:
|
|
270
274
|
|
|
271
|
-
```
|
|
272
|
-
'start' received an invalid workflow function. Ensure the Workflow
|
|
275
|
+
```text
|
|
276
|
+
'start' received an invalid workflow function. Ensure the Workflow SDK is configured correctly and the function includes a 'use workflow' directive.
|
|
273
277
|
```
|
|
274
278
|
|
|
275
279
|
Check both of these first:
|
|
@@ -279,7 +283,7 @@ Check both of these first:
|
|
|
279
283
|
|
|
280
284
|
See [start-invalid-workflow-function](/docs/errors/start-invalid-workflow-function) for full examples and fixes.
|
|
281
285
|
|
|
282
|
-
## Next
|
|
286
|
+
## Next steps
|
|
283
287
|
|
|
284
288
|
- Learn more about the [Foundations](/docs/foundations).
|
|
285
289
|
- Check [Errors](/docs/errors) if you encounter issues.
|
|
@@ -9,6 +9,10 @@ related:
|
|
|
9
9
|
- /docs/foundations/workflows-and-steps
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
+
<CopyPrompt
|
|
13
|
+
text="In this Fastify app, run `npm i workflow fastify nitro rollup` and, if using TypeScript, `npm i -D @types/node typescript`. Create `nitro.config.ts` with `modules: ["workflow/nitro"]`, `vercel: { entryFormat: "node" }`, and `routes: { "/**": { handler: "./src/index.ts", format: "node" } }`. Add package scripts `dev: "nitro dev"` and `build: "nitro build"`. Create `workflows/user-signup.ts` with `"use workflow"`, `sleep`, and `"use step"` helpers. Add `src/index.ts` with a Fastify app, POST `/api/signup`, `start(handleUserSignup, [email])`, `await app.ready()`, and an exported request handler. Run `npm run dev`, call `curl -X POST --json '{"email":"hello@example.com"}' http://localhost:3000/api/signup`, and inspect with `npx workflow inspect runs --web`."
|
|
14
|
+
/>
|
|
15
|
+
|
|
12
16
|
This guide will walk through setting up your first workflow in a Fastify app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.
|
|
13
17
|
|
|
14
18
|
---
|
|
@@ -16,7 +20,7 @@ This guide will walk through setting up your first workflow in a Fastify app. Al
|
|
|
16
20
|
<Steps>
|
|
17
21
|
|
|
18
22
|
<Step>
|
|
19
|
-
## Create
|
|
23
|
+
## Create your Fastify project
|
|
20
24
|
|
|
21
25
|
Start by creating a new Fastify project.
|
|
22
26
|
|
|
@@ -112,7 +116,7 @@ To use the Nitro builder, update your `package.json` to include the following sc
|
|
|
112
116
|
|
|
113
117
|
<Step>
|
|
114
118
|
|
|
115
|
-
## Create
|
|
119
|
+
## Create your first workflow
|
|
116
120
|
|
|
117
121
|
Create a new file for our first workflow:
|
|
118
122
|
|
|
@@ -132,13 +136,13 @@ export async function handleUserSignup(email: string) {
|
|
|
132
136
|
}
|
|
133
137
|
```
|
|
134
138
|
|
|
135
|
-
We'll fill in those functions next
|
|
139
|
+
We'll fill in those functions next. The current code does the following:
|
|
136
140
|
|
|
137
141
|
- We define a **workflow** function with the directive `"use workflow"`. Think of the workflow function as the _orchestrator_ of individual **steps**.
|
|
138
142
|
- The Workflow SDK's `sleep` function allows us to suspend execution of the workflow without using up any resources. A sleep can be a few seconds, hours, days, or even months long.
|
|
139
|
-
## Create
|
|
143
|
+
## Create your workflow steps
|
|
140
144
|
|
|
141
|
-
|
|
145
|
+
Define the missing functions:
|
|
142
146
|
```typescript title="workflows/user-signup.ts" lineNumbers
|
|
143
147
|
import { FatalError } from "workflow";
|
|
144
148
|
|
|
@@ -170,7 +174,7 @@ async function sendOnboardingEmail(user: { id: string; email: string }) {
|
|
|
170
174
|
```
|
|
171
175
|
Taking a look at this code:
|
|
172
176
|
|
|
173
|
-
- Business logic lives inside **steps**. When a
|
|
177
|
+
- Business logic lives inside **steps**. When a workflow invokes a step, the workflow suspends while the step runs with full Node.js access. The combined handler may execute it inline; queued retries and continuations return through the same flow route.
|
|
174
178
|
- If a step throws an error, like in `sendWelcomeEmail`, the step will automatically be retried until it succeeds (or hits the step's max retry count).
|
|
175
179
|
- Steps can throw a `FatalError` if an error is intentional and should not be retried.
|
|
176
180
|
|
|
@@ -182,7 +186,7 @@ Taking a look at this code:
|
|
|
182
186
|
|
|
183
187
|
<Step>
|
|
184
188
|
|
|
185
|
-
## Create
|
|
189
|
+
## Create your route handler
|
|
186
190
|
|
|
187
191
|
To invoke your new workflow, we'll create both the Fastify app and a new API route handler at `src/index.ts` with the following code:
|
|
188
192
|
|
|
@@ -243,7 +247,7 @@ npx workflow inspect runs # add '--web' for an interactive Web based UI
|
|
|
243
247
|
|
|
244
248
|
## Deploying to production
|
|
245
249
|
|
|
246
|
-
Workflow SDK apps currently work best when deployed to [Vercel](https://vercel.com/home) and
|
|
250
|
+
Workflow SDK apps currently work best when deployed to [Vercel](https://vercel.com/home) and need no special configuration.
|
|
247
251
|
|
|
248
252
|
<FluidComputeCallout />
|
|
249
253
|
|
|
@@ -255,8 +259,8 @@ Check the [Deploying](/docs/deploying) section to learn how your workflows can b
|
|
|
255
259
|
|
|
256
260
|
If you see this error:
|
|
257
261
|
|
|
258
|
-
```
|
|
259
|
-
'start' received an invalid workflow function. Ensure the Workflow
|
|
262
|
+
```text
|
|
263
|
+
'start' received an invalid workflow function. Ensure the Workflow SDK is configured correctly and the function includes a 'use workflow' directive.
|
|
260
264
|
```
|
|
261
265
|
|
|
262
266
|
Check both of these first:
|
|
@@ -266,7 +270,7 @@ Check both of these first:
|
|
|
266
270
|
|
|
267
271
|
See [start-invalid-workflow-function](/docs/errors/start-invalid-workflow-function) for full examples and fixes.
|
|
268
272
|
|
|
269
|
-
## Next
|
|
273
|
+
## Next steps
|
|
270
274
|
|
|
271
275
|
- Learn more about the [Foundations](/docs/foundations).
|
|
272
276
|
- Check [Errors](/docs/errors) if you encounter issues.
|