workflow 5.0.0-beta.9 → 5.0.1
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 +3 -1
- package/dist/api-workflow.d.ts.map +1 -1
- package/dist/api-workflow.js +2 -1
- package/dist/api.d.ts +5 -4
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +6 -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 +4 -4
- package/dist/internal/builtins.js +6 -6
- 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/runtime.d.ts +2 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +4 -1
- package/docs/advanced/dynamic-workflows.mdx +227 -0
- package/docs/advanced/index.mdx +13 -0
- package/docs/advanced/meta.json +5 -0
- 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 +16 -12
- package/docs/ai/index.mdx +67 -72
- 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 +68 -15
- package/docs/api-reference/workflow/create-hook.mdx +170 -10
- package/docs/api-reference/workflow/create-webhook.mdx +16 -15
- package/docs/api-reference/workflow/define-hook.mdx +41 -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 +3 -3
- package/docs/api-reference/workflow/retryable-error.mdx +1 -1
- package/docs/api-reference/workflow/set-attributes.mdx +63 -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 +37 -15
- package/docs/api-reference/workflow-api/get-run.mdx +43 -8
- package/docs/api-reference/workflow-api/index.mdx +8 -9
- package/docs/api-reference/workflow-api/register-lifecycle-hooks.mdx +87 -0
- package/docs/api-reference/workflow-api/resume-hook.mdx +77 -12
- package/docs/api-reference/workflow-api/resume-webhook.mdx +15 -9
- package/docs/api-reference/workflow-api/start.mdx +107 -12
- 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-force-claimed-error.mdx +70 -0
- package/docs/api-reference/workflow-errors/hook-not-found-error.mdx +8 -8
- package/docs/api-reference/workflow-errors/index.mdx +91 -0
- package/docs/api-reference/workflow-errors/meta.json +7 -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 +13 -6
- package/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +12 -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 +19 -11
- package/docs/api-reference/workflow-nest/configure-workflow-controller.mdx +37 -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 +46 -0
- package/docs/api-reference/workflow-nest/workflow-module.mdx +134 -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 +51 -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 +133 -35
- 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 +53 -41
- 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 +89 -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 +424 -0
- package/docs/configuration/worlds.mdx +341 -0
- package/docs/cookbook/advanced/child-workflows.mdx +33 -25
- 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 +35 -31
- package/docs/cookbook/agent-patterns/agent-cancellation.mdx +78 -60
- package/docs/cookbook/agent-patterns/durable-agent.mdx +23 -135
- package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +180 -195
- package/docs/cookbook/common-patterns/batching.mdx +20 -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 +30 -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 +12 -7
- package/docs/cookbook/common-patterns/workflow-composition.mdx +27 -17
- package/docs/cookbook/index.mdx +22 -22
- package/docs/cookbook/integrations/ai-sdk.mdx +63 -48
- package/docs/cookbook/integrations/chat-sdk.mdx +46 -33
- package/docs/cookbook/integrations/sandbox.mdx +58 -45
- package/docs/deploying.mdx +106 -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 +38 -11
- package/docs/errors/hook-force-claimed.mdx +96 -0
- package/docs/errors/index.mdx +24 -37
- 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 +54 -11
- package/docs/foundations/hooks.mdx +187 -36
- package/docs/foundations/idempotency.mdx +267 -12
- package/docs/foundations/index.mdx +1 -26
- package/docs/foundations/serialization.mdx +22 -22
- package/docs/foundations/starting-workflows.mdx +104 -30
- package/docs/foundations/streaming.mdx +108 -60
- package/docs/foundations/versioning.mdx +4 -4
- package/docs/foundations/workflows-and-steps.mdx +10 -10
- 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 +264 -21
- package/docs/getting-started/next.mdx +18 -14
- 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 +82 -66
- package/docs/how-it-works/encryption.mdx +30 -26
- package/docs/how-it-works/event-sourcing.mdx +132 -35
- 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 +4 -2
- package/docs/observability/attributes.mdx +91 -21
- package/docs/observability/index.mdx +29 -15
- package/docs/observability/lifecycle-hooks.mdx +95 -0
- 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 +118 -38
- package/docs/testing/server-based.mdx +10 -10
- package/docs/whats-new.mdx +196 -0
- package/docs/worlds/building-a-world.mdx +600 -0
- package/docs/worlds/local.mdx +129 -0
- package/docs/worlds/meta.json +10 -0
- package/docs/worlds/postgres.mdx +428 -0
- package/docs/worlds/upgrading-to-v5.mdx +183 -0
- package/docs/worlds/vercel.mdx +389 -0
- package/package.json +17 -14
- package/docs/api-reference/workflow/experimental-set-attributes.mdx +0 -63
- 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 -181
- 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 -358
- package/docs/migration-guides/migrating-from-inngest.mdx +0 -304
- package/docs/migration-guides/migrating-from-temporal.mdx +0 -313
- package/docs/migration-guides/migrating-from-trigger-dev.mdx +0 -328
|
@@ -9,10 +9,15 @@ related:
|
|
|
9
9
|
- /docs/foundations/workflows-and-steps
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
<CopyPrompt
|
|
13
|
+
text="In this NestJS app, run `npm i workflow @workflow/nest` and `npm i -D @swc/cli @swc/core`. Configure `nest-cli.json` with `compilerOptions.builder: "swc"` and `deleteOutDir: true`. Run `npx @workflow/nest init`, add `.swcrc` to `.gitignore`, and set package scripts `prebuild: "npx @workflow/nest init --force"` and `start:dev: "npx @workflow/nest init --force && nest start --watch"`. Import `WorkflowModule.forRoot()` from `@workflow/nest` in `src/app.module.ts` (use `{ moduleType: "commonjs", distDir: "dist" }` if compiling CommonJS). Create `src/workflows/user-signup.ts` with `"use workflow"`, `sleep`, and `"use step"` helpers. Add a `POST /signup` controller method that reads `email`, calls `start(handleUserSignup, [email])` from `workflow/api`, and returns JSON. Run `npm run start:dev`, call `curl -X POST --json '{"email":"hello@example.com"}' http://localhost:3000/signup`, and inspect with `npx workflow web` or `npx workflow inspect runs`."
|
|
14
|
+
/>
|
|
15
|
+
|
|
16
|
+
Set up your first durable workflow in a NestJS app and learn the core Workflow SDK concepts.
|
|
13
17
|
|
|
14
18
|
<Callout>
|
|
15
|
-
NestJS integration is experimental
|
|
19
|
+
NestJS integration is experimental. Deployment to Vercel is supported via the
|
|
20
|
+
`workflow-nest build --vercel` command. See [Deploy to Vercel](#deploy-to-vercel) below.
|
|
16
21
|
</Callout>
|
|
17
22
|
|
|
18
23
|
---
|
|
@@ -20,7 +25,7 @@ NestJS integration is experimental and not yet supported for deployment to Verce
|
|
|
20
25
|
<Steps>
|
|
21
26
|
|
|
22
27
|
<Step>
|
|
23
|
-
## Create
|
|
28
|
+
## Create your NestJS project
|
|
24
29
|
|
|
25
30
|
Start by creating a new NestJS project using the NestJS CLI.
|
|
26
31
|
|
|
@@ -41,7 +46,7 @@ cd my-workflow-app
|
|
|
41
46
|
npm i workflow @workflow/nest
|
|
42
47
|
```
|
|
43
48
|
|
|
44
|
-
### Choose
|
|
49
|
+
### Choose your module format
|
|
45
50
|
|
|
46
51
|
NestJS projects using `@workflow/nest` can compile as either ESM or CommonJS. Choose the setup that matches your SWC output instead of assuming ESM is required.
|
|
47
52
|
|
|
@@ -107,7 +112,7 @@ Ensure your `nest-cli.json` has SWC as the builder:
|
|
|
107
112
|
}
|
|
108
113
|
```
|
|
109
114
|
|
|
110
|
-
### Initialize SWC
|
|
115
|
+
### Initialize SWC configuration
|
|
111
116
|
|
|
112
117
|
Run the init command to generate the SWC configuration:
|
|
113
118
|
|
|
@@ -115,7 +120,7 @@ Run the init command to generate the SWC configuration:
|
|
|
115
120
|
npx @workflow/nest init
|
|
116
121
|
```
|
|
117
122
|
|
|
118
|
-
This creates a `.swcrc` file configured with the Workflow SWC plugin for
|
|
123
|
+
This creates a `.swcrc` file configured with the Workflow SWC plugin for step-mode transformations.
|
|
119
124
|
|
|
120
125
|
<Callout>
|
|
121
126
|
Add `.swcrc` to your `.gitignore` as it contains machine-specific absolute paths that shouldn't be committed.
|
|
@@ -138,10 +143,10 @@ Add scripts to regenerate the SWC configuration before builds:
|
|
|
138
143
|
<Accordion type="single" collapsible>
|
|
139
144
|
<AccordionItem value="typescript-intellisense" className="[&_h3]:my-0">
|
|
140
145
|
<AccordionTrigger className="[&_p]:my-0 text-lg [&_p]:text-foreground">
|
|
141
|
-
|
|
146
|
+
Set up IntelliSense for TypeScript (optional)
|
|
142
147
|
</AccordionTrigger>
|
|
143
148
|
<AccordionContent className="[&_p]:my-2">
|
|
144
|
-
To enable helpful hints in your IDE,
|
|
149
|
+
To enable helpful hints in your IDE, set up the workflow plugin in `tsconfig.json`:
|
|
145
150
|
|
|
146
151
|
```json title="tsconfig.json" lineNumbers
|
|
147
152
|
{
|
|
@@ -203,7 +208,7 @@ The `WorkflowModule` handles workflow bundle building and provides HTTP routing
|
|
|
203
208
|
|
|
204
209
|
<Step>
|
|
205
210
|
|
|
206
|
-
## Create
|
|
211
|
+
## Create your first workflow
|
|
207
212
|
|
|
208
213
|
Create a new file for our first workflow in the `src/workflows` directory:
|
|
209
214
|
|
|
@@ -236,14 +241,14 @@ export async function handleUserSignup(email: string) {
|
|
|
236
241
|
}
|
|
237
242
|
```
|
|
238
243
|
|
|
239
|
-
We'll fill in those functions next, but
|
|
244
|
+
We'll fill in those functions next, but first review this code:
|
|
240
245
|
|
|
241
246
|
- We define a **workflow** function with the directive `"use workflow"`. Think of the workflow function as the _orchestrator_ of individual **steps**.
|
|
242
247
|
- 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.
|
|
243
248
|
|
|
244
|
-
## Create
|
|
249
|
+
## Create your workflow steps
|
|
245
250
|
|
|
246
|
-
|
|
251
|
+
Define the missing functions.
|
|
247
252
|
|
|
248
253
|
```typescript title="src/workflows/user-signup.ts" lineNumbers
|
|
249
254
|
import { FatalError } from "workflow";
|
|
@@ -284,7 +289,7 @@ async function sendOnboardingEmail(user: { id: string; email: string }) {
|
|
|
284
289
|
|
|
285
290
|
Taking a look at this code:
|
|
286
291
|
|
|
287
|
-
- Business logic lives inside **steps**. When a
|
|
292
|
+
- 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.
|
|
288
293
|
- 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).
|
|
289
294
|
- Steps can throw a `FatalError` if an error is intentional and should not be retried.
|
|
290
295
|
|
|
@@ -297,7 +302,7 @@ Taking a look at this code:
|
|
|
297
302
|
|
|
298
303
|
<Step>
|
|
299
304
|
|
|
300
|
-
## Create
|
|
305
|
+
## Create your controller
|
|
301
306
|
|
|
302
307
|
To invoke your new workflow, update your controller with a new endpoint:
|
|
303
308
|
|
|
@@ -357,11 +362,67 @@ npx workflow inspect runs
|
|
|
357
362
|
|
|
358
363
|
</Step>
|
|
359
364
|
|
|
365
|
+
<Step>
|
|
366
|
+
|
|
367
|
+
## Deploy to Vercel
|
|
368
|
+
|
|
369
|
+
Because NestJS is not a Vercel-native framework, the Workflow SDK produces a
|
|
370
|
+
[Build Output API](https://vercel.com/docs/build-output-api) directory for you. This emits the
|
|
371
|
+
workflow queue-consumer function (registered with `experimentalTriggers` so Vercel's queue can
|
|
372
|
+
discover it) alongside your NestJS app as a catch-all function. Without it, workflow runs stay
|
|
373
|
+
`pending` because nothing consumes the queue.
|
|
374
|
+
|
|
375
|
+
Add a Vercel-specific entry module that default-exports your Nest app's underlying Node handler:
|
|
376
|
+
|
|
377
|
+
```typescript title="_vercel/entry.ts" lineNumbers
|
|
378
|
+
import 'reflect-metadata';
|
|
379
|
+
import { NestFactory } from '@nestjs/core';
|
|
380
|
+
import type { NestExpressApplication } from '@nestjs/platform-express';
|
|
381
|
+
import express from 'express';
|
|
382
|
+
import { AppModule } from '../dist/app.module.js';
|
|
383
|
+
|
|
384
|
+
let ready: Promise<express.Express> | undefined;
|
|
385
|
+
|
|
386
|
+
async function createHandler() {
|
|
387
|
+
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
|
|
388
|
+
bodyParser: false,
|
|
389
|
+
});
|
|
390
|
+
app.use(express.json());
|
|
391
|
+
await app.init();
|
|
392
|
+
return app.getHttpAdapter().getInstance();
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export default async function handler(req: express.Request, res: express.Response) {
|
|
396
|
+
ready ??= createHandler();
|
|
397
|
+
return (await ready)(req, res);
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
No module change is needed for the in-process build: `skipBuild` defaults to `true` when the
|
|
402
|
+
`VERCEL` environment variable is set, because the Build Output already contains the bundles and
|
|
403
|
+
the deployed filesystem is read-only.
|
|
404
|
+
|
|
405
|
+
Set your build command so the Build Output is produced after `nest build`:
|
|
406
|
+
|
|
407
|
+
```json title="package.json" lineNumbers
|
|
408
|
+
{
|
|
409
|
+
"scripts": {
|
|
410
|
+
"vercel-build": "workflow-nest init --force && nest build && workflow-nest build --vercel --dirs src/workflows --entry _vercel/entry.ts"
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Deploy as usual. `workflow-nest build --vercel` runs automatically on Vercel (it also detects the
|
|
416
|
+
`VERCEL` environment variable), writing `.vercel/output` with the consumer function so your runs
|
|
417
|
+
execute instead of staying `pending`.
|
|
418
|
+
|
|
419
|
+
</Step>
|
|
420
|
+
|
|
360
421
|
</Steps>
|
|
361
422
|
|
|
362
423
|
---
|
|
363
424
|
|
|
364
|
-
## Configuration
|
|
425
|
+
## Configuration options
|
|
365
426
|
|
|
366
427
|
The `WorkflowModule.forRoot()` method accepts optional configuration:
|
|
367
428
|
|
|
@@ -375,7 +436,9 @@ WorkflowModule.forRoot({
|
|
|
375
436
|
// Output directory for generated bundles (default: '.nestjs/workflow')
|
|
376
437
|
outDir: '.nestjs/workflow',
|
|
377
438
|
|
|
378
|
-
// Skip building
|
|
439
|
+
// Skip building when bundles are pre-built with `workflow-nest build`
|
|
440
|
+
// (default: true when VERCEL is set, false otherwise). Startup fails if the
|
|
441
|
+
// bundles are missing.
|
|
379
442
|
skipBuild: false,
|
|
380
443
|
|
|
381
444
|
// SWC module type: 'es6' (default) or 'commonjs'
|
|
@@ -387,24 +450,203 @@ WorkflowModule.forRoot({
|
|
|
387
450
|
// Should match the outDir in your tsconfig.json
|
|
388
451
|
distDir: 'dist',
|
|
389
452
|
|
|
390
|
-
// Source maps on generated workflow bundles (default: 'inline'
|
|
453
|
+
// Source maps on generated workflow bundles (default: 'inline' in
|
|
454
|
+
// development, false in production).
|
|
391
455
|
// Accepts the same values as esbuild's sourcemap option: true, false,
|
|
392
456
|
// 'inline', 'linked', 'external', 'both'. Set to false for smaller
|
|
393
|
-
// function bundles (useful for staying under the Vercel
|
|
457
|
+
// function bundles (useful for staying under the Vercel 250 MB function
|
|
394
458
|
// size limit) at the cost of stack traces pointing at generated code.
|
|
395
459
|
// Can also be set via the WORKFLOW_SOURCEMAP environment variable.
|
|
396
460
|
sourcemap: 'inline',
|
|
461
|
+
|
|
462
|
+
// Route prefix the workflow endpoints are served under. Leave unset to adopt
|
|
463
|
+
// app.setGlobalPrefix() automatically; set it when a reverse proxy mounts the
|
|
464
|
+
// app on a sub-path NestJS does not know about. See "Global prefixes" below.
|
|
465
|
+
basePath: '/api',
|
|
466
|
+
|
|
467
|
+
// Start the target World's background workers with the app and close them on
|
|
468
|
+
// shutdown. Self-hosted Worlds (for example @workflow/world-postgres) need
|
|
469
|
+
// this or runs are created and never picked up. Leave off on Vercel.
|
|
470
|
+
manageWorldLifecycle: false,
|
|
471
|
+
|
|
472
|
+
// Load the generated bundles during startup instead of on the first request
|
|
473
|
+
// (default: true, or false when VERCEL is set because dedicated functions
|
|
474
|
+
// serve the bundles there).
|
|
475
|
+
preloadBundles: true,
|
|
476
|
+
});
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
Options can also come from other providers with `forRootAsync`:
|
|
480
|
+
|
|
481
|
+
{/*@skip-typecheck - Configuration snippet, imports shown above*/}
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
WorkflowModule.forRootAsync({
|
|
485
|
+
imports: [ConfigModule],
|
|
486
|
+
inject: [ConfigService],
|
|
487
|
+
useFactory: (config: ConfigService) => ({
|
|
488
|
+
basePath: config.get('API_PREFIX'),
|
|
489
|
+
}),
|
|
397
490
|
});
|
|
398
491
|
```
|
|
399
492
|
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## Global prefixes and sub-paths
|
|
496
|
+
|
|
497
|
+
`app.setGlobalPrefix()` moves the workflow routes. The SDK has to generate its
|
|
498
|
+
queue callback and webhook URLs under the same prefix, or every delivery 404s and
|
|
499
|
+
runs stay `pending`.
|
|
500
|
+
|
|
501
|
+
`WorkflowModule` reads the global prefix during startup and adopts it, so this
|
|
502
|
+
works with no configuration:
|
|
503
|
+
|
|
504
|
+
{/*@skip-typecheck - Bootstrap snippet*/}
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
const app = await NestFactory.create(AppModule);
|
|
508
|
+
app.setGlobalPrefix('api'); // workflow URLs become /api/.well-known/workflow/v1/...
|
|
509
|
+
await app.listen(3000);
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
Set `basePath` explicitly when the prefix is applied outside NestJS, for example
|
|
513
|
+
by a reverse proxy that strips it before the request reaches your app. An
|
|
514
|
+
explicit `basePath` wins over the global prefix, and a disagreement between the
|
|
515
|
+
two is logged at startup.
|
|
516
|
+
|
|
517
|
+
For Vercel, pass the same value to the build so the deployed queue-consumer
|
|
518
|
+
function generates matching URLs:
|
|
519
|
+
|
|
520
|
+
```bash
|
|
521
|
+
workflow-nest build --vercel --base-path /api
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
<Callout type="warn">
|
|
525
|
+
`app.enableVersioning()` moves the workflow routes the same way, and is not
|
|
526
|
+
handled automatically. Exclude the workflow controller from versioning, or mount
|
|
527
|
+
it where the SDK expects it.
|
|
528
|
+
</Callout>
|
|
529
|
+
|
|
530
|
+
---
|
|
531
|
+
|
|
532
|
+
## Raw request bodies
|
|
533
|
+
|
|
534
|
+
The workflow routes are a byte pipe. A webhook that verifies a signature over its
|
|
535
|
+
raw body (Stripe, GitHub, Shopify, Slack) only works if the bytes the sender
|
|
536
|
+
signed reach the workflow unchanged, and a body parser that has already turned
|
|
537
|
+
the request into an object destroys them.
|
|
538
|
+
|
|
539
|
+
Create the app with `rawBody` so the original bytes stay available:
|
|
540
|
+
|
|
541
|
+
{/*@skip-typecheck - Bootstrap snippet*/}
|
|
542
|
+
|
|
543
|
+
```typescript
|
|
544
|
+
const app = await NestFactory.create(AppModule, { rawBody: true });
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Without it, `@workflow/nest` falls back to re-serializing the parsed body with
|
|
548
|
+
`JSON.stringify` and logs a warning once. That changes whitespace and key order,
|
|
549
|
+
so signature verification fails.
|
|
550
|
+
|
|
551
|
+
Content types no body parser claims (XML, `application/x-www-form-urlencoded`
|
|
552
|
+
without the parser registered, custom media types) are read straight from the
|
|
553
|
+
request stream and need no configuration.
|
|
554
|
+
|
|
555
|
+
<Callout type="info">
|
|
556
|
+
On Vercel the webhook route is served by its own function rather than by your
|
|
557
|
+
NestJS app, so this only affects self-hosted deployments.
|
|
558
|
+
</Callout>
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## NestJS dependency injection is not available in workflows and steps
|
|
563
|
+
|
|
564
|
+
Workflows and steps do not run inside your NestJS application. They are compiled
|
|
565
|
+
into separate bundles, so **the Nest injector, your providers, and anything
|
|
566
|
+
built on the request context are out of reach from `"use workflow"` and
|
|
567
|
+
`"use step"` code.**
|
|
568
|
+
|
|
569
|
+
Concretely, none of these work inside a step:
|
|
570
|
+
|
|
571
|
+
- injecting a provider, or resolving one with `app.get(MyService)`
|
|
572
|
+
- `@Injectable()` classes reached through a module-level reference to the app
|
|
573
|
+
- request-scoped providers, and `AsyncLocalStorage` context such as `nestjs-cls`
|
|
574
|
+
- guards, interceptors, pipes, and filters
|
|
575
|
+
- the Nest `Logger`
|
|
576
|
+
|
|
577
|
+
Two mechanics cause this, and neither has a workaround:
|
|
578
|
+
|
|
579
|
+
1. A step's bundle gets **its own copy** of any application file it imports.
|
|
580
|
+
Module-level state is therefore duplicated, and a class imported into a step
|
|
581
|
+
is a different class object from the one your module registered. Nest uses the
|
|
582
|
+
class itself as the injection token, so `app.get(MyService)` from a step
|
|
583
|
+
raises `UnknownElementException` even when it can reach the app.
|
|
584
|
+
2. On Vercel, workflows and steps run in the queue-consumer function, a separate
|
|
585
|
+
function from the one serving your NestJS app. There is no shared process to
|
|
586
|
+
reach into.
|
|
587
|
+
|
|
588
|
+
Write steps as plain functions over their arguments, and keep the wiring in your
|
|
589
|
+
controllers and providers:
|
|
590
|
+
|
|
591
|
+
{/*@skip-typecheck - Illustrates the pattern, not a complete app*/}
|
|
592
|
+
|
|
593
|
+
```typescript
|
|
594
|
+
// A step takes what it needs as arguments and builds its own clients.
|
|
595
|
+
async function chargeCustomer(customerId: string, cents: number) {
|
|
596
|
+
'use step';
|
|
597
|
+
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
|
|
598
|
+
return await stripe.charges.create({ customer: customerId, amount: cents });
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// The controller stays a normal Nest controller with normal DI.
|
|
602
|
+
@Controller('billing')
|
|
603
|
+
export class BillingController {
|
|
604
|
+
constructor(private readonly customers: CustomerService) {}
|
|
605
|
+
|
|
606
|
+
@Post('charge')
|
|
607
|
+
async charge(@Body() body: { id: string }) {
|
|
608
|
+
const customer = await this.customers.find(body.id);
|
|
609
|
+
// Pass plain, serializable values into the workflow.
|
|
610
|
+
await start(billingWorkflow, [customer.id, customer.planCents]);
|
|
611
|
+
return { started: true };
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
Configuration a step needs should come from the environment rather than
|
|
617
|
+
`ConfigService`, and shared logic should live in plain modules that a step can
|
|
618
|
+
import without pulling in `@nestjs/common`.
|
|
619
|
+
|
|
620
|
+
<Callout type="info">
|
|
621
|
+
Importing a file that uses `@nestjs/common` into a step is supported and builds
|
|
622
|
+
correctly, it simply gives you a class with no injected dependencies. Prefer
|
|
623
|
+
plain functions so the intent is clear.
|
|
624
|
+
</Callout>
|
|
625
|
+
|
|
626
|
+
---
|
|
627
|
+
|
|
628
|
+
## Production checklist
|
|
629
|
+
|
|
630
|
+
- Run `workflow-nest build` in your build step and set `skipBuild: true`, or
|
|
631
|
+
leave `skipBuild` unset so the bundles are built during startup. With
|
|
632
|
+
`skipBuild` set and no bundles present, startup fails with an explicit error
|
|
633
|
+
rather than serving broken workflow routes.
|
|
634
|
+
- Create the app with `{ rawBody: true }` if you receive signed webhooks.
|
|
635
|
+
- Set `basePath` (or rely on the adopted global prefix) so generated URLs match
|
|
636
|
+
the routes NestJS serves.
|
|
637
|
+
- Set `manageWorldLifecycle: true` for a self-hosted World, and call
|
|
638
|
+
`app.enableShutdownHooks()` so the World is closed on a signal.
|
|
639
|
+
- `skipBuild` defaults to `true` when the `VERCEL` environment variable is set,
|
|
640
|
+
so no Vercel-specific branch is needed in your module configuration.
|
|
641
|
+
|
|
400
642
|
## Troubleshooting
|
|
401
643
|
|
|
402
644
|
### `start()` says it received an invalid workflow function
|
|
403
645
|
|
|
404
646
|
If you see this error:
|
|
405
647
|
|
|
406
|
-
```
|
|
407
|
-
'start' received an invalid workflow function. Ensure the Workflow
|
|
648
|
+
```text
|
|
649
|
+
'start' received an invalid workflow function. Ensure the Workflow SDK is configured correctly and the function includes a 'use workflow' directive.
|
|
408
650
|
```
|
|
409
651
|
|
|
410
652
|
Check both of these first:
|
|
@@ -413,7 +655,8 @@ Check both of these first:
|
|
|
413
655
|
2. Your NestJS app imports and registers the `WorkflowModule`.
|
|
414
656
|
|
|
415
657
|
See [start-invalid-workflow-function](/docs/errors/start-invalid-workflow-function) for full examples and fixes.
|
|
416
|
-
|
|
658
|
+
|
|
659
|
+
## Next steps
|
|
417
660
|
|
|
418
661
|
- Learn more about the [Foundations](/docs/foundations).
|
|
419
662
|
- Check [Errors](/docs/errors) if you encounter issues.
|
|
@@ -7,13 +7,17 @@ prerequisites:
|
|
|
7
7
|
- /docs/getting-started
|
|
8
8
|
related:
|
|
9
9
|
- /docs/api-reference/workflow-next
|
|
10
|
-
- /
|
|
10
|
+
- /worlds/vercel
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
<CopyPrompt
|
|
14
|
+
text="In this Next.js app, run `npm i workflow`. Wrap `next.config.ts` with `withWorkflow` from `workflow/next`. If the app has `proxy.ts` or middleware, exclude `.well-known/workflow/` from its matcher. Add `workflows/user-signup.ts` exporting `handleUserSignup(email)` with `"use workflow"`, `sleep` from `workflow`, and `"use step"` helper functions that create a user, send a welcome email, and send an onboarding email. Add `app/api/signup/route.ts` with a POST handler that reads `{ email }`, calls `start(handleUserSignup, [email])` from `workflow/api`, and returns JSON. Run `npm run dev`, trigger `curl -X POST --json '{"email":"hello@example.com"}' http://localhost:3000/api/signup`, and inspect with `npx workflow web` or `npx workflow inspect runs`."
|
|
15
|
+
/>
|
|
16
|
+
|
|
13
17
|
<Steps>
|
|
14
18
|
|
|
15
19
|
<Step>
|
|
16
|
-
## Create
|
|
20
|
+
## Create your Next.js project
|
|
17
21
|
|
|
18
22
|
Start by creating a new Next.js project. This command will create a new directory named `my-workflow-app` and set up a Next.js project inside it.
|
|
19
23
|
|
|
@@ -85,7 +89,7 @@ If your Next.js app has a [proxy handler](https://nextjs.org/docs/app/api-refere
|
|
|
85
89
|
(formerly known as "middleware"), you'll need to update the matcher pattern to exclude Workflow's
|
|
86
90
|
internal paths to prevent the proxy handler from running on them.
|
|
87
91
|
|
|
88
|
-
If you see `[local world] Queue operation failed` with `Cannot perform ArrayBuffer.prototype.slice on a detached ArrayBuffer`, your proxy matcher is still intercepting Workflow's internal `POST /.well-known/workflow/v1/flow` request. This
|
|
92
|
+
If you see `[local world] Queue operation failed` with `Cannot perform ArrayBuffer.prototype.slice on a detached ArrayBuffer`, your proxy matcher is still intercepting Workflow's internal `POST /.well-known/workflow/v1/flow` request. This issue can be hard to spot in Next.js 16, where `proxy.ts` replaced `middleware.ts`.
|
|
89
93
|
|
|
90
94
|
Add `.well-known/workflow/*` to your matcher exclusion list:
|
|
91
95
|
|
|
@@ -117,7 +121,7 @@ This ensures that internal Workflow paths are not intercepted by your middleware
|
|
|
117
121
|
|
|
118
122
|
<Step>
|
|
119
123
|
|
|
120
|
-
## Create
|
|
124
|
+
## Create your first workflow
|
|
121
125
|
|
|
122
126
|
Create a new file for our first workflow:
|
|
123
127
|
|
|
@@ -140,14 +144,14 @@ export async function handleUserSignup(email: string) {
|
|
|
140
144
|
|
|
141
145
|
```
|
|
142
146
|
|
|
143
|
-
We'll fill in those functions next
|
|
147
|
+
We'll fill in those functions next. The current code does the following:
|
|
144
148
|
|
|
145
149
|
* We define a **workflow** function with the directive `"use workflow"`. Think of the workflow function as the _orchestrator_ of individual **steps**.
|
|
146
150
|
* 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.
|
|
147
151
|
|
|
148
|
-
## Create
|
|
152
|
+
## Create your workflow steps
|
|
149
153
|
|
|
150
|
-
|
|
154
|
+
Define the missing functions.
|
|
151
155
|
|
|
152
156
|
```typescript title="workflows/user-signup.ts" lineNumbers
|
|
153
157
|
import { FatalError } from "workflow"
|
|
@@ -188,7 +192,7 @@ async function sendOnboardingEmail(user: { id: string; email: string}) {
|
|
|
188
192
|
|
|
189
193
|
Taking a look at this code:
|
|
190
194
|
|
|
191
|
-
* Business logic lives inside **steps**. When a
|
|
195
|
+
* 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.
|
|
192
196
|
* 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).
|
|
193
197
|
* Steps can throw a `FatalError` if an error is intentional and should not be retried.
|
|
194
198
|
|
|
@@ -200,7 +204,7 @@ We'll dive deeper into workflows, steps, and other ways to suspend or handle eve
|
|
|
200
204
|
|
|
201
205
|
<Step>
|
|
202
206
|
|
|
203
|
-
## Create
|
|
207
|
+
## Create your route handler
|
|
204
208
|
|
|
205
209
|
To invoke your new workflow, we'll need to add your workflow to a `POST` API Route Handler, `app/api/signup/route.ts`, with the following code:
|
|
206
210
|
|
|
@@ -272,12 +276,12 @@ Check the [Deploying](/docs/deploying) section to learn how your workflows can b
|
|
|
272
276
|
|
|
273
277
|
If you see this error when upgrading to Next.js 16.1 or later:
|
|
274
278
|
|
|
275
|
-
```
|
|
279
|
+
```text
|
|
276
280
|
Build error occurred
|
|
277
281
|
Error: Cannot find module 'next/dist/lib/server-external-packages.json'
|
|
278
282
|
```
|
|
279
283
|
|
|
280
|
-
Upgrade
|
|
284
|
+
Upgrade `workflow` to the latest release:
|
|
281
285
|
|
|
282
286
|
```package-install
|
|
283
287
|
workflow@latest
|
|
@@ -311,8 +315,8 @@ Without this configuration, you may experience intermittent issues where workflo
|
|
|
311
315
|
|
|
312
316
|
If you see this error:
|
|
313
317
|
|
|
314
|
-
```
|
|
315
|
-
'start' received an invalid workflow function. Ensure the Workflow
|
|
318
|
+
```text
|
|
319
|
+
'start' received an invalid workflow function. Ensure the Workflow SDK is configured correctly and the function includes a 'use workflow' directive.
|
|
316
320
|
```
|
|
317
321
|
|
|
318
322
|
Check both of these first:
|
|
@@ -322,7 +326,7 @@ Check both of these first:
|
|
|
322
326
|
|
|
323
327
|
See [start-invalid-workflow-function](/docs/errors/start-invalid-workflow-function) for full examples and fixes.
|
|
324
328
|
|
|
325
|
-
## Next
|
|
329
|
+
## Next steps
|
|
326
330
|
|
|
327
331
|
* Learn more about the [Foundations](/docs/foundations).
|
|
328
332
|
* Check [Errors](/docs/errors) if you encounter issues.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Nitro
|
|
3
|
-
description:
|
|
3
|
+
description: Set up your first durable workflow in a Nitro v3 project.
|
|
4
4
|
type: guide
|
|
5
5
|
summary: Set up Workflow SDK in a Nitro app.
|
|
6
6
|
prerequisites:
|
|
@@ -9,12 +9,16 @@ related:
|
|
|
9
9
|
- /docs/foundations/workflows-and-steps
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
+
<CopyPrompt
|
|
13
|
+
text="In this Nitro app, run `npm i workflow`. In `nitro.config.ts`, use `defineConfig` from `nitro`, set `serverDir: "./server"`, and add `modules: ["workflow/nitro"]`. Add the TypeScript plugin `{ "name": "workflow" }` to `tsconfig.json` if TypeScript is used. Create `workflows/user-signup.ts` with `handleUserSignup(email)`, `"use workflow"`, `sleep` from `workflow`, and `"use step"` helpers. Add `server/api/signup.post.ts` using `defineEventHandler` from `nitro/h3` and `start` from `workflow/api` to start the workflow from `{ email }`. Run `npm run dev`, call `curl -X POST --json '{"email":"hello@example.com"}' http://localhost:3000/api/signup`, then inspect with `npx workflow web` or `npx workflow inspect runs`."
|
|
14
|
+
/>
|
|
15
|
+
|
|
12
16
|
<Steps>
|
|
13
17
|
|
|
14
18
|
<Step>
|
|
15
|
-
## Create
|
|
19
|
+
## Create your Nitro project
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
Create a [Nitro v3](https://v3.nitro.build/) project in a new directory named `nitro-app`:
|
|
18
22
|
|
|
19
23
|
```bash
|
|
20
24
|
npx create-nitro-app
|
|
@@ -34,7 +38,7 @@ npm i workflow
|
|
|
34
38
|
|
|
35
39
|
### Configure Nitro
|
|
36
40
|
|
|
37
|
-
Add `workflow/nitro` module to your `nitro.config.ts
|
|
41
|
+
Add the `workflow/nitro` module to your `nitro.config.ts`. This enables the `"use workflow"` and `"use step"` directives.
|
|
38
42
|
|
|
39
43
|
```typescript title="nitro.config.ts" lineNumbers
|
|
40
44
|
import { defineConfig } from "nitro";
|
|
@@ -66,15 +70,15 @@ export default defineConfig({
|
|
|
66
70
|
| --- | --- | --- | --- |
|
|
67
71
|
| `dirs` | `string[]` | — | Directories to scan for workflows and steps. By default, `workflows/` is scanned from the project root and all layer source directories. |
|
|
68
72
|
| `runtime` | `string` | `'nodejs22.x'` | Node.js runtime version for Vercel Functions (e.g. `'nodejs22.x'`, `'nodejs24.x'`). |
|
|
69
|
-
| `sourcemap` | `boolean \| 'inline' \| 'linked' \| 'external' \| 'both'` | `'inline'` | Controls source maps on generated workflow bundles. Accepts the same values as esbuild's `sourcemap` option.
|
|
73
|
+
| `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. |
|
|
70
74
|
|
|
71
75
|
<Accordion type="single" collapsible>
|
|
72
76
|
<AccordionItem value="typescript-intellisense" className="[&_h3]:my-0">
|
|
73
77
|
<AccordionTrigger className="[&_p]:my-0 text-lg [&_p]:text-foreground">
|
|
74
|
-
|
|
78
|
+
Set up IntelliSense for TypeScript (optional)
|
|
75
79
|
</AccordionTrigger>
|
|
76
80
|
<AccordionContent className="[&_p]:my-2">
|
|
77
|
-
To enable helpful hints in your IDE,
|
|
81
|
+
To enable helpful hints in your IDE, set up the workflow plugin in `tsconfig.json`:
|
|
78
82
|
|
|
79
83
|
```json title="tsconfig.json" lineNumbers
|
|
80
84
|
{
|
|
@@ -98,7 +102,7 @@ To enable helpful hints in your IDE, setup the workflow plugin in `tsconfig.json
|
|
|
98
102
|
|
|
99
103
|
<Step>
|
|
100
104
|
|
|
101
|
-
## Create
|
|
105
|
+
## Create your first workflow
|
|
102
106
|
|
|
103
107
|
Create a new file for our first workflow:
|
|
104
108
|
|
|
@@ -120,14 +124,14 @@ export async function handleUserSignup(email: string) {
|
|
|
120
124
|
}
|
|
121
125
|
```
|
|
122
126
|
|
|
123
|
-
We'll fill in those functions next, but
|
|
127
|
+
We'll fill in those functions next, but first review this code:
|
|
124
128
|
|
|
125
129
|
- We define a **workflow** function with the directive `"use workflow"`. Think of the workflow function as the _orchestrator_ of individual **steps**.
|
|
126
130
|
- 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.
|
|
127
131
|
|
|
128
|
-
## Create
|
|
132
|
+
## Create your workflow steps
|
|
129
133
|
|
|
130
|
-
|
|
134
|
+
Define the missing functions.
|
|
131
135
|
|
|
132
136
|
```typescript title="workflows/user-signup.ts" lineNumbers
|
|
133
137
|
import { FatalError } from "workflow";
|
|
@@ -168,7 +172,7 @@ async function sendOnboardingEmail(user: { id: string; email: string }) {
|
|
|
168
172
|
|
|
169
173
|
Taking a look at this code:
|
|
170
174
|
|
|
171
|
-
- Business logic lives inside **steps**. When a
|
|
175
|
+
- 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.
|
|
172
176
|
- 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).
|
|
173
177
|
- Steps can throw a `FatalError` if an error is intentional and should not be retried.
|
|
174
178
|
|
|
@@ -181,7 +185,7 @@ Taking a look at this code:
|
|
|
181
185
|
|
|
182
186
|
<Step>
|
|
183
187
|
|
|
184
|
-
## Create
|
|
188
|
+
## Create your route handler
|
|
185
189
|
|
|
186
190
|
To invoke your new workflow, we'll create a new API route handler at `server/api/signup.post.ts` with the following code:
|
|
187
191
|
|
|
@@ -200,7 +204,7 @@ export default defineEventHandler(async ({ req }) => {
|
|
|
200
204
|
});
|
|
201
205
|
```
|
|
202
206
|
|
|
203
|
-
This
|
|
207
|
+
This route handler creates a `POST` request endpoint at `/api/signup` that triggers your workflow.
|
|
204
208
|
|
|
205
209
|
<Callout>
|
|
206
210
|
Workflows can be triggered from API routes or any server-side
|
|
@@ -244,7 +248,7 @@ npx workflow inspect runs
|
|
|
244
248
|
|
|
245
249
|
## Deploying to production
|
|
246
250
|
|
|
247
|
-
Workflow SDK apps currently work best when deployed to [Vercel](https://vercel.com/home) and
|
|
251
|
+
Workflow SDK apps currently work best when deployed to [Vercel](https://vercel.com/home) and need no special configuration.
|
|
248
252
|
|
|
249
253
|
<FluidComputeCallout />
|
|
250
254
|
|
|
@@ -256,8 +260,8 @@ Check the [Deploying](/docs/deploying) section to learn how your workflows can b
|
|
|
256
260
|
|
|
257
261
|
If you see this error:
|
|
258
262
|
|
|
259
|
-
```
|
|
260
|
-
'start' received an invalid workflow function. Ensure the Workflow
|
|
263
|
+
```text
|
|
264
|
+
'start' received an invalid workflow function. Ensure the Workflow SDK is configured correctly and the function includes a 'use workflow' directive.
|
|
261
265
|
```
|
|
262
266
|
|
|
263
267
|
Check both of these first:
|
|
@@ -267,7 +271,7 @@ Check both of these first:
|
|
|
267
271
|
|
|
268
272
|
See [start-invalid-workflow-function](/docs/errors/start-invalid-workflow-function) for full examples and fixes.
|
|
269
273
|
|
|
270
|
-
## Next
|
|
274
|
+
## Next steps
|
|
271
275
|
|
|
272
276
|
- Learn more about the [Foundations](/docs/foundations).
|
|
273
277
|
- Check [Errors](/docs/errors) if you encounter issues.
|