---
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
---

# Astro





<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 `{ &#x22;name&#x22;: &#x22;workflow&#x22; }` to `tsconfig.json` if TypeScript is used. Create `src/workflows/user-signup.ts` exporting `handleUserSignup(email)` with `&#x22;use workflow&#x22;`, `sleep` from `workflow`, and `&#x22;use step&#x22;` 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 '{&#x22;email&#x22;:&#x22;hello@example.com&#x22;}' 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`

    <CodeBlockTabs defaultValue="npm">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="npm">
          npm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="pnpm">
          pnpm
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="yarn">
          yarn
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="bun">
          bun
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="npm">
        ```bash
        npm i workflow
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pnpm">
        ```bash
        pnpm add workflow
        ```
      </CodeBlockTab>

      <CodeBlockTab value="yarn">
        ```bash
        yarn add workflow
        ```
      </CodeBlockTab>

      <CodeBlockTab value="bun">
        ```bash
        bun add workflow
        ```
      </CodeBlockTab>
    </CodeBlockTabs>

    ### 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()`](/docs/api-reference/workflow-astro/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. |

    <Accordion type="single" collapsible>
      <AccordionItem value="typescript-intellisense" className="[&_h3]:my-0">
        <AccordionTrigger className="text-sm">
          ### Set up IntelliSense for TypeScript (optional)
        </AccordionTrigger>

        <AccordionContent className="[&_p]:my-2">
          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]
                }
              ]
            }
          }
          ```
        </AccordionContent>
      </AccordionItem>
    </Accordion>
  </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 step is invoked inside a **workflow**, it gets enqueued to run on a separate request while the workflow is suspended, as it does for `sleep`.
    * 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.

You can also use the [Workflow 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
```

<img alt="Workflow SDK Web UI" src={__img0} placeholder="blur" />

***

## 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 Development Kit 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.
* See the [`workflow/astro` reference](/docs/api-reference/workflow-astro) for integration options.
* Explore the [API Reference](/docs/api-reference).


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)