Skip to content

Hooks

Hooks let you observe the lifecycle of a flow without putting logging, metrics, and debugging logic inside the steps themselves.

They are one of the main ways to make workflows operationally transparent.

What hooks are for

Hooks are useful for:

  • structured logging
  • metrics collection
  • tracing integration
  • alerts and monitoring
  • debugging execution order

They are not the place for core business logic. Your workflow should still behave correctly even if every hook is removed.

Defining hooks

You can define hooks when creating a flow:

ts
const flow = create("my-flow", {
  hooks: {
    onFlowStart: ({ flowName }) => console.log(`Flow ${flowName} started`),
    onFlowComplete: ({ result }) => console.log(`Flow completed with status ${result.status}`),
    onFlowFail: ({ result }) => console.error("Flow failed", result),
    onStepStart: ({ stepName }) => console.log(`Step ${stepName} started`),
    onStepComplete: ({ stepName, result }) => console.log(`Step ${stepName} completed`, result),
    onStepFail: ({ stepName, error }) => console.error(`Step ${stepName} failed`, error),
    onCompensate: ({ stepName }) => console.log(`Compensating ${stepName}`),
    onCompensateComplete: ({ stepName }) => console.log(`Compensation finished for ${stepName}`),
  }
});

Available hooks

HookDescription
onFlowStartTriggered when flow execution begins.
onFlowCompleteTriggered when the flow completes successfully or is cancelled via the normal completion path.
onFlowFailTriggered when the flow fails.
onStepStartTriggered before a step starts executing.
onStepCompleteTriggered after a step completes successfully.
onStepFailTriggered after a step fails.
onCompensateTriggered before a compensation function runs.
onCompensateCompleteTriggered after a compensation function completes.

What hook payloads contain

Most hook payloads include:

  • flowName
  • input
  • context

Step-level hooks also include the step name and step-specific result or error details.

This gives you enough information to produce high-quality logs or metrics without reaching into global state.

Important behavior

Hook failures are swallowed internally.

That means:

  • hooks may throw
  • hooks may be async
  • a broken hook does not break the workflow

This design keeps observability code from becoming a source of business failures.

Practical examples

Logging

ts
onStepComplete: ({ flowName, stepName, result }) => {
  logger.info({ flowName, stepName, result });
}

Metrics

ts
onFlowComplete: ({ flowName, result }) => {
  metrics.timing(`flows.${flowName}.duration_ms`, result.durationMs);
}

Error reporting

ts
onStepFail: ({ flowName, stepName, error }) => {
  errorTracker.captureException(error, {
    tags: { flowName, stepName }
  });
}

Best Practices

  • Keep hooks side-effect-light and operational in nature.
  • Prefer structured logs over string logs.
  • Use hooks to observe steps, not to decide business flow behavior.
  • Include correlation identifiers from input or context in your telemetry.

Released under the MIT License.