惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

人人都是产品经理
人人都是产品经理
Stack Overflow Blog
Stack Overflow Blog
L
LINUX DO - 最新话题
Google Online Security Blog
Google Online Security Blog
Schneier on Security
Schneier on Security
Spread Privacy
Spread Privacy
www.infosecurity-magazine.com
www.infosecurity-magazine.com
雷峰网
雷峰网
Google DeepMind News
Google DeepMind News
Microsoft Azure Blog
Microsoft Azure Blog
IT之家
IT之家
V
Vulnerabilities – Threatpost
K
Kaspersky official blog
S
Schneier on Security
B
Blog
The Register - Security
The Register - Security
SecWiki News
SecWiki News
Hacker News: Ask HN
Hacker News: Ask HN
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
S
Security Affairs
T
The Blog of Author Tim Ferriss
G
Google Developers Blog
T
Tenable Blog
P
Proofpoint News Feed
Apple Machine Learning Research
Apple Machine Learning Research
D
DataBreaches.Net
S
Secure Thoughts
Security Latest
Security Latest
H
Heimdal Security Blog
The Hacker News
The Hacker News
O
OpenAI News
AWS News Blog
AWS News Blog
量子位
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
腾讯CDC
U
Unit 42
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
T
The Exploit Database - CXSecurity.com
NISL@THU
NISL@THU
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Hugging Face - Blog
Hugging Face - Blog
The Last Watchdog
The Last Watchdog
Recorded Future
Recorded Future
V2EX - 技术
V2EX - 技术
爱范儿
爱范儿
F
Full Disclosure

Workflow SDK Documentation

Patterns for Defining Tools Human-in-the-Loop Building Durable AI Agents Queueing User Messages Resumable Streams Sleep, Suspense, and Scheduling Streaming Updates from Tools API Reference Workflow Globals Changelog Resilient run start Cookbook Building a World Deploying Astro Express Fastify Hono Getting Started Next.js Nitro Nuxt Python SvelteKit Vite corrupted-event-log fetch-in-workflow hook-conflict Errors node-js-module-in-workflow serialization-failed start-invalid-workflow-function step-not-registered timeout-in-workflow webhook-invalid-respond-with-value webhook-response-not-sent workflow-not-registered Errors & Retrying Hooks & Webhooks Idempotency Foundations Serialization Starting Workflows Streaming Versioning Workflows and Steps How the Directives Work Encryption Event Sourcing Framework Integrations Understanding Directives Migration Guides Migrating from AWS Step Functions Migrating from Inngest Migrating from Temporal Observability Testing Server-Based Testing createHook createWebhook defineHook FatalError fetch getStepMetadata getWorkflowMetadata getWritable workflow RetryableError sleep @workflow/vitest DurableAgent @workflow/ai WorkflowChatTransport getHookByToken getRun getWorld workflow/api resumeHook resumeWebhook Chat Session Modeling runtime-decryption-failed Upgrading Workflows abort-signal-timeout-in-workflow Cancellation How Cancellation Works Internal Serializable AbortController and AbortSignal Eager Processing of Steps & Incremental Event Replay TanStack Start Agent Cancellation Sequential & Parallel Execution Workflow Composition Local World | Workflow SDK Postgres World | Workflow SDK Vercel World | Workflow SDK Migrating from trigger.dev Secure Credential Handling Local World | Workflow SDK Postgres World | Workflow SDK Vercel World | Workflow SDK
NestJS
2026-05-31 · via Workflow SDK Documentation

Set up your first durable workflow in a NestJS application.

This guide will walk through setting up your first workflow in a NestJS app. Along the way, you'll learn more about the concepts that are fundamental to using the Workflow SDK in your own projects.

NestJS integration is experimental and not yet supported for deployment to Vercel.


Start by creating a new NestJS project using the NestJS CLI.

npm i -g @nestjs/cli
nest new my-workflow-app

Enter the newly made directory:

Install workflow

Choose Your Module Format

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.

ESM (default)

Use this when your NestJS project is configured as an ES module app.

{
  "name": "my-workflow-app",
  "type": "module"
}

When using ESM with NestJS, local imports must include the .js extension (e.g., import { AppModule } from './app.module.js'). This applies even though your source files are .ts.

CommonJS

Use this when your NestJS project compiles CommonJS via SWC.

import { Module } from '@nestjs/common';
import { WorkflowModule } from '@workflow/nest';

@Module({
  imports: [
    WorkflowModule.forRoot({
      moduleType: 'commonjs',
      distDir: 'dist',
    }),
  ],
})
export class AppModule {}

distDir should match the directory where NestJS writes compiled .js files. In the default SWC setup, that is dist.

Configure NestJS to use SWC

NestJS supports SWC as an alternative compiler for faster builds. The Workflow SDK uses an SWC plugin to transform workflow files.

Install the required SWC packages:

Ensure your nest-cli.json has SWC as the builder:

{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "builder": "swc",
    "deleteOutDir": true
  }
}

Initialize SWC Configuration

Run the init command to generate the SWC configuration:

This creates a .swcrc file configured with the Workflow SWC plugin for client-mode transformations.

Add .swcrc to your .gitignore as it contains machine-specific absolute paths that shouldn't be committed.

Update package.json

Add scripts to regenerate the SWC configuration before builds:

{
  "scripts": {
    "prebuild": "npx @workflow/nest init --force",
    "build": "nest build",
    "start:dev": "npx @workflow/nest init --force && nest start --watch"
  }
}

In your app.module.ts, import the WorkflowModule and keep the module format you chose above.

import { Module } from '@nestjs/common';
import { WorkflowModule } from '@workflow/nest';
import { AppController } from './app.controller.js';
import { AppService } from './app.service.js';

@Module({
  imports: [WorkflowModule.forRoot()], 
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

If you chose CommonJS above, keep the CommonJS options here as well:

WorkflowModule.forRoot({
  moduleType: 'commonjs',
  distDir: 'dist',
})

The .js local import specifiers in this example are the ESM form.

The WorkflowModule handles workflow bundle building and provides HTTP routing for workflow execution at .well-known/workflow/v1/.

Create a new file for our first workflow in the src/workflows directory:

Workflow files must be inside the src/ directory so they get compiled with the SWC plugin that enables the start() function to work correctly.

If start() says it received an invalid workflow function, check both of these first:

  1. The workflow function includes "use workflow".
  2. The workflow file lives inside src/ so NestJS compiles it with the SWC plugin.

See start-invalid-workflow-function for full examples and fixes.

import { sleep } from "workflow";

export async function handleUserSignup(email: string) {
  "use workflow"; 

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

Let's now define those missing functions.

import { FatalError } from "workflow";

// Our workflow function defined earlier

async function createUser(email: string) {
  "use step"; 

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

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

  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.

To invoke your new workflow, update your controller with a new endpoint:

import { Body, Controller, Post } from '@nestjs/common';
import { start } from 'workflow/api';
import { handleUserSignup } from './workflows/user-signup.js';

@Controller()
export class AppController {
  @Post('signup')
  async signup(@Body() body: { email: string }) {
    await start(handleUserSignup, [body.email]);
    return { message: 'User signup workflow started' };
  }
}

If you chose CommonJS above, use the same local import style as the rest of your NestJS app here too. The .js extension shown in this example is the ESM form.

This creates a POST endpoint at /signup that will trigger your workflow.

To start your development server, run the following command in your terminal:

Once your development server is running, you can trigger your workflow by running this command in the terminal:

curl -X POST -H "Content-Type: application/json" -d '{"email":"hello@example.com"}' http://localhost:3000/signup

Check the NestJS 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 to inspect your workflow runs and steps in detail.

# Open the observability Web UI
npx workflow web
# or if you prefer a terminal interface, use the CLI inspect command
npx workflow inspect runs

Workflow SDK Web UI


The WorkflowModule.forRoot() method accepts optional configuration:

WorkflowModule.forRoot({
  // Directory to scan for workflow files (default: ['src'])
  dirs: ['src'],

  // Output directory for generated bundles (default: '.nestjs/workflow')
  outDir: '.nestjs/workflow',

  // Skip building in production when bundles are pre-built
  skipBuild: false,

  // SWC module type: 'es6' (default) or 'commonjs'
  // Set to 'commonjs' if your NestJS project compiles to CJS via SWC
  moduleType: 'es6',

  // Directory where NestJS compiles .ts to .js (default: 'dist')
  // Only used when moduleType is 'commonjs'
  // Should match the outDir in your tsconfig.json
  distDir: 'dist',

  // Source maps on generated workflow bundles (default: 'inline').
  // Accepts the same values as esbuild's sourcemap option: true, false,
  // 'inline', 'linked', 'external', 'both'. Set to false for smaller
  // function bundles (useful for staying under the Vercel 250MB function
  // size limit) at the cost of stack traces pointing at generated code.
  // Can also be set via the WORKFLOW_SOURCEMAP environment variable.
  sourcemap: 'inline',
});

start() says it received an invalid workflow function

If you see this error:

'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 NestJS app imports and registers the WorkflowModule.

See start-invalid-workflow-function for full examples and fixes.