diff --git a/docs/app/(home)/components/frameworks.tsx b/docs/app/(home)/components/frameworks.tsx index f04e8e18d..ad3c14ea8 100644 --- a/docs/app/(home)/components/frameworks.tsx +++ b/docs/app/(home)/components/frameworks.tsx @@ -6,6 +6,177 @@ import type { ComponentProps } from 'react'; import { toast } from 'sonner'; import { Badge } from '@/components/ui/badge'; +export const AstroDark = (props: ComponentProps<'svg'>) => ( + + + + + + + + + + + + + + + + +); + +export const AstroLight = (props: ComponentProps<'svg'>) => ( + + + + + + + + + + + +); + +export const AstroGray = (props: ComponentProps<'svg'>) => ( + + + + + + + + + + + +); + +export const TanStack = (props: ComponentProps<'svg'>) => ( + + + + + + + + + + +); + +export const TanStackGray = (props: ComponentProps<'svg'>) => ( + + + +); + +export const Vite = (props: ComponentProps<'svg'>) => ( + + Vite + + + + + + + + + + + + + + +); + export const Nitro = (props: ComponentProps<'svg'>) => ( { with the frameworks you already use with more coming soon. -
+
{ + + + + @@ -562,11 +737,19 @@ export const Frameworks = () => {
handleRequest('Bun')} + className="group relative cursor-pointer size-[48px]" + onClick={() => handleRequest('TanStack')} + > + + +
+
handleRequest('Astro')} > - - + + +
diff --git a/docs/content/docs/getting-started/index.mdx b/docs/content/docs/getting-started/index.mdx index f52a9a565..5a66dc856 100644 --- a/docs/content/docs/getting-started/index.mdx +++ b/docs/content/docs/getting-started/index.mdx @@ -2,7 +2,7 @@ title: Getting Started --- -import { Next, Nitro, SvelteKit, Nuxt, Hono, Bun } from '@/app/(home)/components/frameworks'; +import { Next, Nitro, SvelteKit, Nuxt, Hono, Bun, AstroDark, AstroLight, TanStack, Vite } from '@/app/(home)/components/frameworks'; # Getting Started @@ -13,6 +13,10 @@ Start by choosing your framework. Each guide will walk you through the steps to Next.js + + + Vite + Hono @@ -29,4 +33,15 @@ Start by choosing your framework. Each guide will walk you through the steps to Nuxt + + + + Astro + Coming soon + + + + TanStack Start + Coming soon + diff --git a/docs/content/docs/getting-started/meta.json b/docs/content/docs/getting-started/meta.json index b91640441..5c5ee87c8 100644 --- a/docs/content/docs/getting-started/meta.json +++ b/docs/content/docs/getting-started/meta.json @@ -1,5 +1,5 @@ { "title": "Getting Started", - "pages": ["next", "..."], + "pages": ["next", "vite", "..."], "defaultOpen": true } diff --git a/docs/content/docs/getting-started/vite.mdx b/docs/content/docs/getting-started/vite.mdx new file mode 100644 index 000000000..a79456788 --- /dev/null +++ b/docs/content/docs/getting-started/vite.mdx @@ -0,0 +1,246 @@ +--- +title: Vite +--- + +# Vite + +This guide will walk through setting up your first workflow in a Vite app. Along the way, you'll learn more about the concepts that are fundamental to using the development kit in your own projects. + +--- + + + + +## Create Your Vite Project + +Start by creating a new Vite project. This command will create a new directory named `my-workflow-app` with a minimal setup and setup a Vite project inside it. + +```bash +npm create vite@latest my-workflow-app -- --template react-ts +``` + +Enter the newly made directory: + +```bash +cd my-workflow-app +``` + +### Install `workflow` and `nitro` + + + + + npm i workflow nitro + + + + + pnpm i workflow nitro + + + + + yarn add workflow nitro + + + + + +While Vite provides the build tooling and development server, Nitro adds the server framework needed for API routes and deployment. Together they enable building full-stack applications with workflow support. Learn more about Nitro [here](https://v3.nitro.build). + + +### Configure Vite + +Add `workflow()` to your Vite config. This enables usage of the `"use workflow"` and `"use step"` directives. + +```typescript title="vite.config.ts" lineNumbers +import { nitro } from 'nitro/vite'; +import { defineConfig } from 'vite'; +import { workflow } from 'workflow/vite'; + +export default defineConfig({ + plugins: [nitro(), workflow()], // [!code highlight] + nitro: { // [!code highlight] + serverDir: './', // [!code highlight] + }, // [!code highlight] +}); +``` + + + + + ### Setup IntelliSense for TypeScript (Optional) + + + +To enable helpful hints in your IDE, setup the workflow plugin in `tsconfig.json`: + +```json title="tsconfig.json" lineNumbers +{ + "compilerOptions": { + // ... rest of your TypeScript config + "plugins": [ + { + "name": "workflow" // [!code highlight] + } + ] + } +} +``` + + + + + + + + + +## 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); + + return { userId: user.id, status: "onboarded" }; +} + +``` + +We'll fill in those functions next, but let's take a look at 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 DevKit'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 + +Let's now define those 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 step is invoked inside a **workflow**, it gets enqueued to run on a separate request while the workflow is suspended, just like `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. + + +We'll dive deeper into workflows, steps, and other ways to suspend or handle events in [Foundations](/docs/foundations). + + + + + + +## Create Your Route Handler + +To invoke your new workflow, we'll have to add your workflow to a `POST` API route handler, `api/signup.post.ts` with the following code: + +```typescript title="api/signup.post.ts" +import { start } from 'workflow/api'; +import { defineEventHandler } from 'nitro/h3'; +import { handleUserSignup } from "../workflows/user-signup"; + +export default defineEventHandler(async ({ req }) => { + const { email } = await req.json() as { email: string }; + // Executes asynchronously and doesn't block your app + await start(handleUserSignup, [email]); + return { + message: "User signup workflow started", + } +}); +``` + +This route handler creates a `POST` request endpoint at `/api/signup` that will trigger your workflow. + + +Workflows can be triggered from API routes or any server-side code. + + + + + + +## 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:5173/api/signup +``` + +Check the Vite development server logs to see your workflow execute as well as the steps that are being processed. + +Additionally, you can use the [Workflow DevKit 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 DevKit Web UI + +--- + +## Deploying to production + +Workflow DevKit apps currently work best when deployed to [Vercel](https://vercel.com/home) and needs no special configuration. + +Check the [Deploying](/docs/deploying) section to learn how your workflows can be deployed elsewhere. + +## Next Steps + +* Learn more about the [Foundations](/docs/foundations). +* Check [Errors](/docs/errors) if you encounter issues. +* Explore the [API Reference](/docs/api-reference). diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ff6dd39a9..a0382c01f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,16 +25,16 @@ catalogs: specifier: 5.0.76 version: 5.0.76 esbuild: - specifier: ^0.25.11 + specifier: 0.25.11 version: 0.25.11 nitro: specifier: 3.0.1-alpha.1 version: 3.0.1-alpha.1 typescript: - specifier: ^5.9.3 + specifier: 5.9.3 version: 5.9.3 vitest: - specifier: ^3.2.4 + specifier: 3.2.4 version: 3.2.4 zod: specifier: 4.1.11