workflow
Version:
Workflow SDK - Build durable, resilient, and observable workflows
261 lines (175 loc) • 8.44 kB
text/mdx
---
title: SvelteKit
description: Set up your first durable workflow in a SvelteKit app.
type: guide
summary: Set up Workflow SDK in a SvelteKit app.
prerequisites:
- /docs/getting-started
related:
- /docs/foundations/workflows-and-steps
---
<CopyPrompt
text="In this SvelteKit app, run `npm i workflow`. In `vite.config.ts`, import `workflowPlugin` from `workflow/sveltekit` and add it to `plugins` with `sveltekit()`. Add the TypeScript plugin `{ "name": "workflow" }` to `tsconfig.json` if TypeScript is used. Create `workflows/user-signup.ts` exporting `handleUserSignup(email)` with `"use workflow"`, `sleep` from `workflow`, and `"use step"` helpers that create a user and send emails. Add `src/routes/api/signup/+server.ts` with a POST `RequestHandler` that reads `{ email }`, calls `start(handleUserSignup, [email])` from `workflow/api`, and returns `json({ message: "User signup workflow started" })`. Run `npm run dev`, call `curl -X POST --json '{"email":"hello@example.com"}' http://localhost:5173/api/signup`, then inspect with `npx workflow web` or `npx workflow inspect runs`."
/>
<Steps>
<Step>
## Create your SvelteKit project
Create a minimal SvelteKit project in a new directory named `my-workflow-app`:
```bash
npx sv create my-workflow-app --template=minimal --types=ts --no-add-ons
```
Enter the newly made directory:
```bash
cd my-workflow-app
```
### Install `workflow`
```package-install
npm i workflow
```
### Configure Vite
Add `workflowPlugin()` to your Vite config. This enables usage of the `"use workflow"` and `"use step"` directives.
```typescript title="vite.config.ts" lineNumbers
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
import { workflowPlugin } from "workflow/sveltekit"; // [!code highlight]
export default defineConfig({
plugins: [sveltekit(), workflowPlugin()], // [!code highlight]
});
```
`workflowPlugin()` 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="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);
console.log("Workflow is complete! Run 'npx workflow web' to inspect your run")
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="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/routes/api/signup/+server.ts` with the following code:
```typescript title="src/routes/api/signup/+server.ts"
import { start } from "workflow/api";
import { handleUserSignup } from "../../../../workflows/user-signup";
import { json, type RequestHandler } from "@sveltejs/kit";
export const POST: RequestHandler = async ({
request,
}: {
request: Request;
}) => {
const { email } = await request.json();
// Executes asynchronously and doesn't block your app
await start(handleUserSignup, [email]);
return json({ message: "User signup workflow started" });
};
```
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 SvelteKit 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:5173/api/signup
```
Check the SvelteKit 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
# Open the observability Web UI
npx workflow web
# or if you prefer a terminal interface, use the CLI inspect command
npx workflow inspect runs
```

## Deploying to production
Workflow SDK apps currently work best when deployed to [Vercel](https://vercel.com/home) and need no special configuration.
<FluidComputeCallout />
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 `vite.config.ts` includes the `workflow/sveltekit` plugin.
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).