UNPKG

workflow

Version:

Workflow SDK - Build durable, resilient, and observable workflows

268 lines (179 loc) • 8.45 kB
--- title: Astro description: Set up your first durable workflow in an Astro application. type: guide summary: Set up Workflow SDK in an Astro app. prerequisites: - /docs/getting-started related: - /docs/foundations/workflows-and-steps --- <CopyPrompt 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 `{ &quot;name&quot;: &quot;workflow&quot; }` to `tsconfig.json` if TypeScript is used. Create `src/workflows/user-signup.ts` exporting `handleUserSignup(email)` with `&quot;use workflow&quot;`, `sleep` from `workflow`, and `&quot;use step&quot;` 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 '{&quot;email&quot;:&quot;hello@example.com&quot;}' http://localhost:4321/api/signup`, and inspect with `npx workflow inspect runs`." /> Set up your first durable workflow in an Astro app and learn the core Workflow SDK concepts. --- <Steps> <Step> ## Create your Astro project Create an Astro project in a new directory named `my-workflow-app`: ```bash npm create astro@latest my-workflow-app -- --template minimal --install --yes ``` Enter the newly made directory: ```bash cd my-workflow-app ``` ### Install `workflow` ```package-install npm i workflow ``` ### Configure Astro Add `workflow()` to your Astro config. This enables usage of the `"use workflow"` and `"use step"` directives. ```typescript title="astro.config.mjs" lineNumbers // @ts-check import { defineConfig } from "astro/config"; import { workflow } from "workflow/astro"; // https://astro.build/config export default defineConfig({ integrations: [workflow()], }); ``` `workflow()` accepts an options object: | Option | Type | Default | Description | | --- | --- | --- | --- | | `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. | <Details> <Summary className="[&_h3]:my-0"> ### Set up IntelliSense for TypeScript (optional) </Summary> To enable helpful hints in your IDE, set up the workflow plugin in `tsconfig.json`: ```json title="tsconfig.json" lineNumbers { "compilerOptions": { // ... rest of your TypeScript config "plugins": [ { "name": "workflow" // [!code highlight] } ] } } ``` </Details> </Step> <Step> ## Create your first workflow Create a new file for our first workflow: ```typescript title="src/workflows/user-signup.ts" lineNumbers import { sleep } from "workflow"; export async function handleUserSignup(email: string) { "use workflow"; // [!code highlight] const user = await createUser(email); await sendWelcomeEmail(user); await sleep("5s"); // Pause for 5s - doesn't consume any resources await sendOnboardingEmail(user); return { userId: user.id, status: "onboarded" }; } ``` We'll fill in those functions next, but first review this code: * We define a **workflow** function with the directive `"use workflow"`. Think of the workflow function as the _orchestrator_ of individual **steps**. * 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. ## Create your workflow steps Define the missing functions. ```typescript title="src/workflows/user-signup.ts" lineNumbers import { FatalError } from "workflow" // Our workflow function defined earlier async function createUser(email: string) { "use step"; // [!code highlight] console.log(`Creating user with email: ${email}`); // Full Node.js access - database calls, APIs, etc. return { id: crypto.randomUUID(), email }; } async function sendWelcomeEmail(user: { id: string; email: string; }) { "use step"; // [!code highlight] console.log(`Sending welcome email to user: ${user.id}`); if (Math.random() < 0.3) { // By default, steps will be retried for unhandled errors throw new Error("Retryable!"); } } async function sendOnboardingEmail(user: { id: string; email: string}) { "use step"; // [!code highlight] if (!user.email.includes("@")) { // To skip retrying, throw a FatalError instead throw new FatalError("Invalid Email"); } console.log(`Sending onboarding email to user: ${user.id}`); } ``` Taking a look at this code: * 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. * 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). * Steps can throw a `FatalError` if an error is intentional and should not be retried. <Callout> We'll dive deeper into workflows, steps, and other ways to suspend or handle events in [Foundations](/docs/foundations). </Callout> </Step> <Step> ## Create your route handler 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: ```typescript title="src/pages/api/signup.ts" import type { APIRoute } from "astro"; import { start } from "workflow/api"; import { handleUserSignup } from "../../workflows/user-signup"; export const POST: APIRoute = async ({ request }: { request: Request }) => { const { email } = await request.json(); // Executes asynchronously and doesn't block your app await start(handleUserSignup, [email]); return Response.json({ message: "User signup workflow started", }); }; export const prerender = false; // Don't prerender this page since it's an API route ``` This route handler creates a `POST` request endpoint at `/api/signup` that will trigger your workflow. <Callout> Workflows can be triggered from API routes or any server-side code. </Callout> </Step> </Steps> ## Run in development To start your development server, run the following command in your terminal in the Vite root directory: ```bash npm run dev ``` Once your development server is running, you can trigger your workflow by running this command in the terminal: ```bash curl -X POST --json '{"email":"hello@example.com"}' http://localhost:4321/api/signup ``` Check the Astro development server logs to see your workflow execute as well as the steps that are being processed. Additionally, you can use the [Workflow SDK CLI or Web UI](/docs/observability) to inspect your workflow runs and steps in detail. ```bash npx workflow inspect runs # or add '--web' for an interactive Web based UI ``` ![Workflow SDK Web UI](/o11y-ui.png) --- ## Deploying to production Workflow SDK apps currently work best when deployed to [Vercel](https://vercel.com/home) and need no special configuration. <FluidComputeCallout /> To deploy your Astro project to Vercel, ensure that the [Astro Vercel adapter](https://docs.astro.build/en/guides/integrations-guide/vercel) is configured: ```bash npx astro add vercel ``` Additionally, check the [Deploying](/docs/deploying) section to learn how your workflows can be deployed elsewhere. ## Troubleshooting ### `start()` says it received an invalid workflow function If you see this error: ```text 'start' received an invalid workflow function. Ensure the Workflow SDK is configured correctly and the function includes a 'use workflow' directive. ``` Check both of these first: 1. The workflow function includes `"use workflow"`. 2. Your `astro.config.mjs` includes the `workflow()` integration. See [start-invalid-workflow-function](/docs/errors/start-invalid-workflow-function) for full examples and fixes. ## Next steps * Learn more about the [Foundations](/docs/foundations). * Check [Errors](/docs/errors) if you encounter issues. * Explore the [API Reference](/docs/api-reference).