Handlers
Define how your saga responds to messages.
.on(messageType)​
Starts a handler definition for a message type:
.on('PaymentCaptured')
.handle(async (msg, state, ctx) => {
return { ...state, status: 'paid' };
})
.when(predicate)​
Adds a state guard (optional):
.on('PaymentCaptured')
.when(state => state.status === 'pending') // Only handle if pending
.handle(async (msg, state, ctx) => {
return { ...state, status: 'paid' };
})
If the predicate returns false, the message is ignored.
.handle(handler)​
The message handler function:
.handle(async (msg, state, ctx) => {
// msg - The incoming message (typed)
// state - Current saga state (typed)
// ctx - Saga context (publish, complete, etc.)
// Do work...
await ctx.publish({ type: 'NextStep', ... });
// Return new state
return { ...state, status: 'updated' };
})
Handler Signature​
type SagaHandler<TMessage, TState> = (
message: TMessage,
state: TState,
context: SagaContext
) => Promise<TState>;
Handler Rules​
- Must return new state - Always return a state object
- Immutable updates - Create new objects, don't mutate
- Async - Handlers are always async
Multiple Handlers​
Chain multiple handlers for different messages:
.on('PaymentCaptured')
.handle(async (msg, state, ctx) => ({ ...state, status: 'paid' }))
.on('PaymentFailed')
.handle(async (msg, state, ctx) => ({ ...state, status: 'failed' }))
.on('OrderShipped')
.when(state => state.status === 'paid')
.handle(async (msg, state, ctx) => {
ctx.complete();
return { ...state, status: 'shipped' };
})
Built-in Message Types​
SagaTimeoutExpired​
Automatically published when a timeout expires:
.on('SagaTimeoutExpired')
.when(state => state.status === 'awaiting_payment')
.handle(async (msg, state, ctx) => {
ctx.complete();
return { ...state, status: 'timed_out' };
})