States and Transitions
States represent where an entity is; transitions define the legal moves between them. Learn about state categories, conditional transitions, and auto-continuation.
State Categories
Every workflow defines three kinds of states:
states: {
finals: [OrderStatus.Completed, OrderStatus.Cancelled],
idles: [OrderStatus.Pending],
failed: OrderStatus.Failed,
}| Category | Meaning | What happens |
|---|---|---|
| finals | Terminal states — workflow is done | Orchestrator returns { status: 'final' } |
| idles | Pause states — waiting for external input | Orchestrator returns { status: 'idle' }, adapter waits for callback |
| failed | Error fallback — entity moves here on unhandled errors | Handler threw, entity auto-transitions to this state |
Defining Transitions
Transitions declare how entities move between states in response to events:
{
event: 'order.submit',
from: [OrderStatus.Pending],
to: OrderStatus.Processing,
}event— the trigger event namefrom— array of valid source states (supports multiple origins)to— the target stateconditions— optional guard functions (see below)
How Transitions Are Resolved
When orchestrator.transit() is called:
- Match the event name against registered transitions
- Check the entity's current state is in the transition's
fromarray - Evaluate all condition functions (if any)
- Execute the
@OnEventhandler - Update the entity to the
tostate - Determine the TransitResult
If multiple transitions match the same event and state but have different to states, the router throws BadRequestException — ambiguous state machines are not allowed.
Conditional Transitions
Add condition functions to gate when a transition fires. All conditions must return true:
{
event: 'order.approve',
from: [OrderStatus.PendingApproval],
to: OrderStatus.Approved,
conditions: [
(entity: Order, payload?: { approved: boolean }) => payload?.approved === true,
],
}Branching with Conditions
Use the same event with different conditions to create branching logic:
// Approve path
{
event: 'order.review',
from: [OrderStatus.PendingApproval],
to: OrderStatus.Approved,
conditions: [(_, payload?: { approved: boolean }) => payload?.approved === true],
},
// Reject path
{
event: 'order.review',
from: [OrderStatus.PendingApproval],
to: OrderStatus.Rejected,
conditions: [(_, payload?: { approved: boolean }) => payload?.approved === false],
},Global Conditions
Workflow-level conditions apply to every transition in the workflow:
@Workflow({
// ...
conditions: [
(entity: Order) => entity.isActive, // must be active for any transition
],
})Auto-Continuation
When a transition lands in a state that is neither idle nor final, the orchestrator automatically looks for the next valid transition and returns { status: 'continued', nextEvent }. The adapter feeds this back into transit() without external intervention.
This enables multi-step workflows to chain automatically:
PENDING (idle) → transit → PROCESSING → auto-continue → SHIPPED (final)Only idle states act as "breakpoints" where the workflow pauses.
Related
- Workflow Definition — the
@Workflowdecorator and full definition shape - Events and Handlers —
@OnEvent,@OnDefault, parameter injection - TransitResult — what
transit()returns
Workflow Definition
The @Workflow decorator is the single source of truth for your state machine - what states exist, how they connect, and what entity service handles persistence.
Events and Handlers
Events trigger state transitions. Handlers run your business logic when a transition fires. NestflowJS discovers and wires everything automatically.