Skip to content

Extension System

waku's extension system provides hooks at every stage of the application lifecycle — from the moment a module is decorated with @module() through initialization, runtime, and eventual shutdown. Each hook is defined as a @runtime_checkable Protocol, so extensions are plain classes that implement one or more of these protocols without needing to inherit from a shared base class.

Lifecycle

stateDiagram-v2
    [*] --> Configure: @module() decoration
    Configure --> Registration: OnModuleConfigure (sync)
    Registration --> ContainerBuild: OnModuleRegistration (sync)
    ContainerBuild --> ModuleInit
    ModuleInit --> AppInit: OnModuleInit (async, topo order)
    AppInit --> AfterInit: OnApplicationInit (async)
    AfterInit --> Running: AfterApplicationInit (async)
    Running --> ModuleDestroy
    ModuleDestroy --> AppShutdown: OnModuleDestroy (async, reverse topo)
    AppShutdown --> [*]: OnApplicationShutdown (async)

    Configure: Configure
    Registration: Registration
    ContainerBuild: Container Build
    ModuleInit: Module Init
    AppInit: App Init
    AfterInit: After Init
    Running: Running
    ModuleDestroy: Module Destroy
    AppShutdown: App Shutdown

Hook Reference

Hook Protocol Sync/Async Level When
Configure OnModuleConfigure sync module During @module() decoration
Registration OnModuleRegistration sync both After all modules collected
Init OnModuleInit async module After container built, topo order
App Init OnApplicationInit async app Before app is fully ready
After Init AfterApplicationInit async app After app is fully ready
Destroy OnModuleDestroy async module During shutdown, reverse topo
Shutdown OnApplicationShutdown async app Final cleanup

Phases

Configure — Synchronous, import-time. Receives mutable ModuleMetadata to add providers, imports, or exports. Do not perform I/O here.

Registration — Synchronous, after all module metadata is collected. Receives the full ModuleMetadataRegistry with cross-module visibility. The only hook usable at both module and application level.

Init — Asynchronous, after the DI container is built. Modules initialize in topological order (dependencies first).

App Init — Asynchronous, after all OnModuleInit hooks complete. The container is built but the application is not yet fully ready.

After Init — Asynchronous, immediately after OnApplicationInit. The application is fully initialized. The built-in ValidationExtension implements this hook.

Destroy — Asynchronous, during shutdown in reverse topological order (dependents first).

Shutdown — Asynchronous, after all OnModuleDestroy hooks complete. Final cleanup.

Further reading

  • Custom Extensions — implementation guide with code examples for every hook
  • Application — application lifecycle and lifespan functions
  • Modules — module system and the @module() decorator