CORE CONCEPTS
Lifecycle Hooks
Extend Provider to get onInit()/onDestroy() lifecycle hooks called automatically by the DI container.
import { Provider } from '@rasenganjs/server'; export class DatabaseService extends Provider { connection?: Connection; async onInit() { this.connection = await createConnection(process.env.DATABASE_URL); } async onDestroy() { await this.connection?.close(); } }
Why Extend Provider at All?
A plain class works fine as a provider — extending Provider is purely opt-in for lifecycle hooks. Gateway (@rasenganjs/ws) and Queue (@rasenganjs/queue) both extend Provider themselves, which is exactly why their onInit()/onDestroy() fire like any other provider's.
Every Provider Is Resolved Eagerly at Boot
Unlike lazy-by-default DI containers, Rasengan Server constructs every declared provider during compile() — not just the ones something happens to inject. This means:
onInit()fires for providers nothing else injects (schedulers, background warmers) — you don't need a dummy injection just to trigger startup.- A broken constructor or an unresolvable dependency fails at boot, not on the first request that happens to touch it.
Hook Order
onInit()— called in registration order, once compilation finishes wiring routes and providers together.onDestroy()— called in reverse registration order during graceful shutdown (SIGTERM/SIGINT, handled automatically bybootstrap()), so dependents clean up before their dependencies do.
Circular Dependency Detection
Constructing a provider whose dependency chain loops back on itself is caught immediately, with the full chain in the error:
[rasengan-server] Circular dependency detected: ServiceA → ServiceB → ServiceA
What Runs When
export class MetricsService extends Provider { async onInit() { console.log('MetricsService ready'); // fires during bootstrap(), before requests are served } async onDestroy() { console.log('MetricsService shutting down'); // fires on SIGTERM/SIGINT } }
App-level hooks registered via app.onInit()/app.onDestroy() on ServerApp itself are separate from provider lifecycle hooks — they're forwarded straight to the underlying Futon instance and fire alongside, not instead of, provider hooks.
