workflow
Version:
Workflow SDK - Build durable, resilient, and observable workflows
143 lines (108 loc) • 7.39 kB
text/mdx
---
title: WorkflowModule
description: NestJS module that builds workflow bundles and registers the workflow controller.
type: reference
summary: Import WorkflowModule.forRoot() in your AppModule to enable workflows in a NestJS app.
prerequisites:
- /docs/getting-started/nestjs
---
NestJS module that provides workflow functionality. It builds the workflow bundles on module initialization (`onModuleInit`) and registers the [`WorkflowController`](/docs/api-reference/workflow-nest/workflow-controller) that serves the workflow runtime routes.
## Usage
Add `WorkflowModule.forRoot()` to the `imports` array of your root module.
```typescript title="src/app.module.ts" lineNumbers
import { Module } from "@nestjs/common";
import { WorkflowModule } from "workflow/nest"; // [!code highlight]
({
imports: [WorkflowModule.forRoot()], // [!code highlight]
})
export class AppModule {}
```
If your NestJS project compiles to CommonJS via SWC, pass `moduleType` and `distDir` so the builder can rewrite imports in the generated bundles:
```typescript title="src/app.module.ts" lineNumbers
import { Module } from "@nestjs/common";
import { WorkflowModule } from "workflow/nest";
({
imports: [
WorkflowModule.forRoot({
moduleType: "commonjs", // [!code highlight]
distDir: "dist", // [!code highlight]
}),
],
})
export class AppModule {}
```
## API signature
### Static methods
#### `forRoot(options?)`
Configures the module and returns a NestJS `DynamicModule` registered as `global`. It provides the resolved options under the `WORKFLOW_MODULE_OPTIONS` token, and (unless `skipBuild` is set) creates a [`NestLocalBuilder`](/docs/api-reference/workflow-nest/nest-local-builder) that builds the workflow bundles when the module initializes.
#### `forRootAsync(options)`
Same as `forRoot`, with the options produced by a factory so they can come from other providers.
{/* @skip-typecheck - config snippet, WorkflowModule imported above */}
```typescript title="src/app.module.ts" lineNumbers
WorkflowModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
basePath: config.get("API_PREFIX"),
}),
});
```
| Parameter | Type | Description |
| --- | --- | --- |
| `imports` | `unknown[]` | Optional. Modules whose exported providers the factory injects. |
| `inject` | `unknown[]` | Optional. Providers passed to `useFactory`, in order. |
| `useFactory` | `(...args) => WorkflowModuleOptions \| Promise<WorkflowModuleOptions>` | Returns the module options. |
### Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| `options` | `WorkflowModuleOptions` | Optional. Configures the workflow build. |
#### WorkflowModuleOptions
Extends [`NestBuilderOptions`](/docs/api-reference/workflow-nest/nest-local-builder#nestbuilderoptions): all builder options are accepted, plus `skipBuild`:
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `skipBuild` | `boolean` | `true` when `VERCEL` is set, else `false` | Skip building workflow bundles on startup. The bundles must already exist; startup fails with an explicit error if they do not. |
| `basePath` | `string` | adopted from `app.setGlobalPrefix()` | Route prefix the workflow endpoints are served under, applied to generated callback and webhook URLs. Set it when a reverse proxy mounts the app on a sub-path NestJS cannot see. |
| `manageWorldLifecycle` | `boolean` | `false` | Start the target World's background workers with the app and close them on shutdown. Required for self-hosted Worlds, which otherwise never pick up runs. |
| `preloadBundles` | `boolean` | `false` when `VERCEL` is set, else `true` | Load the generated bundles during startup instead of on the first request. On Vercel, dedicated functions serve the bundles, so there is nothing to preload. |
| `bypassBodyParser` | `boolean` | `true` | Keep the application's body parsers away from `.well-known/workflow/v1`, so queue deliveries are not rejected by Express's 100 KB limit and signed webhook bodies stay byte-exact. The application's own routes are untouched. On Fastify, where the body limit is enforced per instance rather than per route, nothing is patched and a limit low enough to reject deliveries is reported at startup instead. |
| `workingDir` | `string` | `process.cwd()` | Working directory for the NestJS application. |
| `dirs` | `string[]` | `['src']` | Directories to scan for workflow files. |
| `outDir` | `string` | `'.nestjs/workflow'` (relative to `workingDir`) | Output directory for generated workflow bundles. |
| `watch` | `boolean` | `false` | Deprecated and ignored. Watch mode is not implemented for this builder. Use `nest start --watch`, which re-runs the startup build. |
| `moduleType` | `'es6' \| 'commonjs'` | `'es6'` | SWC module compilation type. Set to `'commonjs'` if your NestJS project compiles to CommonJS (CJS) through SWC. |
| `distDir` | `string` | `'dist'` | Directory where NestJS compiles `.ts` source files to `.js` (relative to `workingDir`). Used when `moduleType` is `'commonjs'`. Should match the `outDir` in your `tsconfig.json`. |
| `sourcemap` | `boolean \| 'inline' \| 'linked' \| 'external' \| 'both'` | `'inline'` (dev) / `false` (prod) | Controls source maps on generated workflow bundles. Defaults to `'inline'` in development and `false` in production. Can also be set via the `WORKFLOW_SOURCEMAP` environment variable. |
### Returns
`forRoot()` and `forRootAsync()` return a `DynamicModule` to include in the `imports` array of your root module.
## Injecting the resolved options
Both factories export the resolved options under `WORKFLOW_MODULE_OPTIONS`:
{/* @skip-typecheck - NestJS decorators require special TypeScript config */}
```typescript title="src/some.service.ts" lineNumbers
import { Inject, Injectable } from "@nestjs/common";
import {
WORKFLOW_MODULE_OPTIONS,
type WorkflowModuleOptions,
} from "workflow/nest";
()
export class SomeService {
constructor(
(WORKFLOW_MODULE_OPTIONS)
private readonly options: WorkflowModuleOptions
) {}
}
```
The older `WORKFLOW_OPTIONS` token resolves to the same value and is kept for compatibility.
## Lifecycle
| Hook | Behaviour |
| --- | --- |
| `onModuleInit` | Takes the application's body parsers off the workflow routes (unless `bypassBodyParser` is `false`), reconciles the base path against `app.setGlobalPrefix()`, builds the bundles (or verifies they exist when `skipBuild` is set), optionally starts the World, and preloads the bundles. |
| `onApplicationShutdown` | Closes the World when `manageWorldLifecycle` is set. Call `app.enableShutdownHooks()` so this runs on a signal. |
<Callout type="warn">
`WorkflowModule` reads NestJS's global prefix and HTTP adapter through the
injector, which only works while a single copy of `/core` is installed.
With two copies the injection tokens differ, prefix handling and the
body-parser bypass both stop working, and a warning is logged at startup.
</Callout>
<Callout type="warn">
Workflows and steps run outside the NestJS injector, so providers cannot be injected into `"use workflow"` or `"use step"` code. See [NestJS dependency injection is not available in workflows and steps](/docs/getting-started/nestjs#nestjs-dependency-injection-is-not-available-in-workflows-and-steps).
</Callout>