Getting Started
This guide walks you through creating your first Moost workflow — a simple order processing pipeline with three linear steps.
Installation
Add the workflow adapter to your Moost project:
bash
npm install @moostjs/event-wfbash
pnpm add @moostjs/event-wfAI Agent Skills (Optional)
Give your AI coding agent (Claude Code, Cursor, etc.) deep knowledge of the workflow API:
bash
npx moostjs-event-wf-skill # project-local
npx moostjs-event-wf-skill --global # globalDefine a Workflow Controller
Create a controller with a workflow entry point and its steps:
ts
// src/order.controller.ts
import { Controller, Injectable } from 'moost'
import {
Step,
Workflow,
WorkflowParam,
WorkflowSchema,
} from '@moostjs/event-wf'
interface TOrderContext {
orderId: string
validated: boolean
charged: boolean
shipped: boolean
}
@Injectable('FOR_EVENT')
@Controller()
export class OrderController {
@WorkflowParam('context')
ctx!: TOrderContext
@Workflow('process-order')
@WorkflowSchema<TOrderContext>([
'validate',
'charge',
'ship',
])
processOrder() {}
@Step('validate')
validate() {
console.log(`Validating order ${this.ctx.orderId}`)
this.ctx.validated = true
}
@Step('charge')
charge() {
console.log(`Charging for order ${this.ctx.orderId}`)
this.ctx.charged = true
}
@Step('ship')
ship() {
console.log(`Shipping order ${this.ctx.orderId}`)
this.ctx.shipped = true
}
}A few things to notice:
@Workflow('process-order')marks the method as a workflow entry point with the IDprocess-order@WorkflowSchemadefines the step execution order — here it's a simple linear sequence@Step('validate')registers a method as a workflow step that can be referenced in schemas@WorkflowParam('context')injects the shared workflow context into a class property- The entry point method body is empty — all logic lives in the steps
Register the Adapter
Wire up the workflow adapter in your application entry point:
ts
// src/main.ts
import { Moost } from 'moost'
import { MoostWf } from '@moostjs/event-wf'
import { OrderController } from './order.controller'
const app = new Moost()
const wf = new MoostWf()
app.adapter(wf)
app.registerControllers(OrderController)
await app.init()
// Start the workflow
const result = await wf.start('process-order', {
orderId: 'ORD-001',
validated: false,
charged: false,
shipped: false,
})
console.log(result.finished) // true
console.log(result.state.context)
// { orderId: 'ORD-001', validated: true, charged: true, shipped: true }wf.start() takes a schema ID and an initial context object. It returns a TFlowOutput with the final state.
Understanding the Output
Every start() or resume() call returns a TFlowOutput object:
ts
{
state: {
schemaId: 'process-order',
context: { orderId: 'ORD-001', validated: true, charged: true, shipped: true },
indexes: [3],
},
finished: true, // workflow completed all steps
stepId: 'ship', // last executed step
interrupt: undefined, // not paused
inputRequired: undefined,
}Key fields:
finished—trueif the workflow ran to completionstate— serializable snapshot (schema ID, context, step position)interrupt—trueif the workflow paused (waiting for input or after a retriable error)inputRequired— present when a step needs input before it can proceed
What's Next?
You've built a linear workflow. From here, explore:
- Steps — accessing context, input, and parametric paths
- Schemas & Flow Control — conditions, loops, and branching
- Pause & Resume — pausing for user input and resuming later