Skip to content

Modules and Providers

Modules and providers are the core building blocks of a bunito application.

Providers

A provider is a value the container can resolve. Most providers are classes:

ts
import { Logger, Provider } from '@bunito/bunito';

@Provider({
  injects: [Logger],
})
class UsersService {
  constructor(private readonly logger: Logger) {}
}

The injects array defines constructor dependencies in order. This keeps runtime behavior explicit and friendly to TypeScript and Bun.

When dependency names are more useful than position, use object-based injections:

ts
@Provider({
  injects: {
    logger: Logger,
  },
})
class UsersService {
  private readonly logger: Logger;

  constructor(options: { logger: Logger }) {
    this.logger = options.logger;
  }
}

Modules

A module groups providers and imports other modules:

ts
import { LoggerModule, Module } from '@bunito/bunito';

@Module({
  imports: [LoggerModule],
  providers: [UsersService],
})
class AppModule {}

Modules are also how feature packages plug into an app. For example, HTTP apps add HTTPModule, and message-driven apps add BrokerModule plus an adapter module.

Export providers when another module should be able to inject them:

ts
@Module({
  providers: [UsersService],
  exports: [UsersService],
})
class UsersModule {}

Resolving Providers Manually

For scripts and small apps, you can create an app from module options and resolve providers directly:

ts
import { App, LoggerModule } from '@bunito/bunito';

const app = await App.create({
  imports: [LoggerModule],
  providers: [UsersService],
});

const usersService = await app.resolve(UsersService);

await app.start();
await app.shutdown();

This pattern is useful for workers, scripts, and tests.

Testing Modules

Tests can compose modules the same way application code does. Use App.start() or App.create() with inline module options, then resolve the provider under test:

ts
import { App } from '@bunito/bunito';

const app = await App.start({
  providers: [UsersService],
});

const users = await app.resolve(UsersService);

expect(users.findAll()).toEqual([]);

await app.shutdown();

Feature packages also provide test modules on the shared Test context. See Testing for Test.ConfigModule, Test.LoggerModule, Test.BunServerModule, and Test.BrokerModule.

Lifecycle Hooks

Providers can define lifecycle hooks:

ts
import { OnAppStart, OnDestroy, OnInit, Provider } from '@bunito/bunito';

@Provider()
class Worker {
  @OnInit()
  onInit(): void {}

  @OnAppStart()
  onAppStart(): void {}

  @OnDestroy()
  onDestroy(): void {}
}

Use them for setup, app-start actions, and teardown. Keep normal application logic in regular methods, and keep hooks idempotent enough that failures are easy to diagnose.

Where To Go Next