CORE CONCEPTS

Lifecycle Hooks

Extend Provider to get onInit()/onDestroy() lifecycle hooks called automatically by the DI container.

A provider with lifecycle hooks
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 by bootstrap()), 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

Observing the full lifecycle
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.

Module Scoping
Middleware