Skip to main content

Lifecycle Overview

The lifecycle is a strictly ordered sequence of stages that runs during bootstrap and shutdown. Every service registers callbacks into these stages; the framework runs them in order, awaiting each stage before proceeding.

LIFECYCLE_STAGES​

export const LIFECYCLE_STAGES = [
"PreInit",
"PostConfig",
"Bootstrap",
"Ready",
"PreShutdown",
"ShutdownStart",
"ShutdownComplete",
] as const;

This is the canonical order. Shutdown stages run in the same array order as startup — PreShutdown before ShutdownStart before ShutdownComplete.

Stage reference​

StageWhenWhat's availableTypical use
PreInitAfter wiring, before configLogger, basic utilsOverride config sources, very early setup
PostConfigAfter config validatedAll config valuesRead config, initialize config-dependent state
BootstrapAfter PostConfigEverythingOpen connections, load data, start timers
ReadyAfter all Bootstrap callbacksEverythingStart serving traffic, start scheduled jobs
PreShutdownOn SIGTERM/SIGINTEverythingStop accepting new work (close listeners)
ShutdownStartAfter PreShutdownEverythingFlush and close resources
ShutdownCompleteAfter ShutdownStartEverythingFinal best-effort cleanup

Full sequence​

What happens if a callback throws?​

During startup stages (PreInit through Ready): the error is treated as fatal. Bootstrap halts and process.exit(1) is called.

During shutdown stages: the framework attempts to continue running remaining callbacks in the stage. Errors in shutdown are logged but do not halt the shutdown sequence.

Late registration​

If you register a callback for a stage that has already completed, the behavior depends on the stage:

  • Startup stages (PreInit → Ready): Callback is called immediately.
  • Shutdown stages (PreShutdown → ShutdownComplete): Callback is silently dropped.

This handles cases where services are created dynamically after initial boot.

See also​

  • Hooks — registration API for all seven stages
  • Execution Order — priority semantics and parallel execution