# Module Configuration (/integrations/nestjs/how-to/module-configuration)



## Overview [#overview]

The KubeMQ transport plugs into NestJS dependency injection through the `KubeMQModule` dynamic module. It exposes two distinct roles that you compose in your `AppModule`:

* **`forRoot` / `forRootAsync`** register the **global server/connection configuration** that the transport shares across the application. They provide the `KUBEMQ_MODULE_OPTIONS` token and a shared `KubeMQClientProxy` under `KUBEMQ_SDK_CLIENT`.
* **`register` / `registerAsync`** create a &#x2A;*named `KubeMQClientProxy`** that you inject into your services via `@Inject(name)`. This is the client you call `send()` and `emit()` on.

A third role, &#x2A;*`forFeature` / `forFeatureAsync`**, derives a *scoped* client from the global connection — every channel gets a `channelPrefix` automatically prepended.

<Callout type="info">
  Two separate things are configured here. The **module** (`KubeMQModule`) wires up DI and outbound **clients**. The inbound **strategy** (`KubeMQServer`) — which dispatches messages to your `@*Handler` methods — is constructed separately and passed to `app.connectMicroservice()`. See [Connecting the microservice](#connecting-the-microservice-server-side) below.
</Callout>

Start a local broker before running any of the examples. The gRPC API the transport speaks to listens on `50000`; the shared HTTP server (dashboard and connector endpoints) is on `9090`:

<RunKubeMQ ports="[50000, 9090]" />

## forRoot / forRootAsync [#forroot--forrootasync]

`forRoot` registers a global KubeMQ configuration shared by the whole application. Pass `isGlobal: true` so any module can resolve the shared client without re-importing `KubeMQModule`.

```typescript title="app.module.ts"
import { Module } from '@nestjs/common';
import { KubeMQModule } from '@kubemq/nestjs-transport';

const address = process.env.KUBEMQ_ADDRESS ?? 'localhost:50000';

@Module({
  imports: [
    KubeMQModule.forRoot({
      address,
      clientId: 'nestjs-module-config-for-root-server',
      isGlobal: true,
      reconnect: {
        maxAttempts: -1,
        initialDelayMs: 1000,
        maxDelayMs: 30000,
        multiplier: 2,
        jitter: 'full',
      },
    }),
  ],
})
export class AppModule {}
```

When configuration must be resolved at runtime — from environment variables, a secrets manager, or any other async source — use `forRootAsync` with an `imports` / `useFactory` / `inject` triple. The factory returns `KubeMQModuleOptions` and receives whatever providers you list in `inject`:

```typescript title="app.module.ts"
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { KubeMQModule, type KubeMQModuleOptions } from '@kubemq/nestjs-transport';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    KubeMQModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (config: ConfigService): KubeMQModuleOptions => ({
        address: config.get('KUBEMQ_ADDRESS', 'localhost:50000'),
        clientId: 'nestjs-module-config-for-root-async-server',
        reconnect: {
          maxAttempts: -1,
          initialDelayMs: 1000,
          maxDelayMs: 30000,
          multiplier: 2,
          jitter: 'full',
        },
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}
```

Instead of `useFactory`, you can supply a provider class. `useClass` implements `KubeMQOptionsFactory` (a class with a `createKubeMQOptions()` method) and is instantiated by Nest; `useExisting` reuses a factory provider that already exists in the DI container.

### Parameters [#parameters]

`forRoot` accepts `KubeMQModuleOptions` (the full [`KubeMQServerOptions`](/integrations/nestjs/reference/configuration#kubemqserveroptions) plus `isGlobal`). `forRootAsync` accepts the async configuration below.

<TypeTable
  type="{
  isGlobal: {
    type: 'boolean',
    default: 'true',
    description: 'Register as a global module so the shared client resolves anywhere without re-importing KubeMQModule.',
  },
  imports: {
    type: 'Array<Type | DynamicModule | ...>',
    default: '[]',
    description: 'Modules to import so their providers are available to the factory (async only).',
  },
  useFactory: {
    type: '(...args) => KubeMQModuleOptions | Promise<KubeMQModuleOptions>',
    description: 'Factory returning the module options. One of useFactory / useClass / useExisting is required.',
  },
  useClass: {
    type: 'Type<KubeMQOptionsFactory>',
    description: 'Class implementing KubeMQOptionsFactory.createKubeMQOptions(); instantiated by Nest.',
  },
  useExisting: {
    type: 'Type<KubeMQOptionsFactory>',
    description: 'An existing provider implementing KubeMQOptionsFactory.',
  },
  inject: {
    type: 'InjectionToken[]',
    default: '[]',
    description: 'Tokens injected as arguments into useFactory.',
  },
}"
/>

## register / registerAsync [#register--registerasync]

`register` creates a **named** `KubeMQClientProxy` and exports it under the token you choose. Give it a `name`, an `address`, and a `clientId`:

```typescript title="app.module.ts"
import { Module } from '@nestjs/common';
import { KubeMQModule } from '@kubemq/nestjs-transport';
import { OrderHandlerService } from './order.handler.js';
import { OrderService } from './order.service.js';
import { SenderService } from './sender.service.js';

const address = process.env.KUBEMQ_ADDRESS ?? 'localhost:50000';

@Module({
  imports: [
    KubeMQModule.forRoot({
      address,
      clientId: 'nestjs-module-config-register-server',
      isGlobal: true,
    }),
    KubeMQModule.register({
      name: 'ORDER_SERVICE',
      address,
      clientId: 'nestjs-module-config-register-client',
    }),
  ],
  providers: [OrderHandlerService, OrderService, SenderService],
})
export class AppModule {}
```

Inject the named client into a service with `@Inject('ORDER_SERVICE')`. The proxy is a NestJS `ClientProxy`, so `send()` returns an Observable — wrap it in `firstValueFrom` to await a value:

```typescript title="sender.service.ts"
import { Injectable, Inject, Logger } from '@nestjs/common';
import { KubeMQClientProxy } from '@kubemq/nestjs-transport';
import { firstValueFrom } from 'rxjs';

@Injectable()
export class SenderService {
  private readonly logger = new Logger(SenderService.name);

  constructor(@Inject('ORDER_SERVICE') private readonly client: KubeMQClientProxy) {}

  async sendEvent(): Promise<void> {
    await this.client.connect();
    this.logger.log('Sending event via named ORDER_SERVICE client...');
    await firstValueFrom(
      this.client.emit('nestjs-module-config.register', {
        orderId: 'ORD-100',
        product: 'Widget',
        quantity: 3,
      }),
    );
    this.logger.log('Event sent successfully');
  }
}
```

<Callout type="info">
  You can inject the client typed as the framework's `ClientProxy` from `@nestjs/microservices` or as the concrete `KubeMQClientProxy`. The latter gives you the `connect()` method shown above; the underlying token is the same.
</Callout>

`registerAsync` resolves client options at runtime, the same way `forRootAsync` does. The factory returns `KubeMQClientOptions` and pulls the address from `ConfigService`:

```typescript title="app.module.ts"
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import {
  KubeMQModule,
  type KubeMQModuleOptions,
  type KubeMQClientOptions,
} from '@kubemq/nestjs-transport';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    KubeMQModule.forRootAsync({
      useFactory: (config: ConfigService): KubeMQModuleOptions => ({
        address: config.get('KUBEMQ_ADDRESS', 'localhost:50000'),
        clientId: 'nestjs-module-config-register-async-server',
      }),
      inject: [ConfigService],
    }),
    KubeMQModule.registerAsync({
      name: 'NOTIFICATION_SERVICE',
      useFactory: (config: ConfigService): KubeMQClientOptions => ({
        address: config.get('KUBEMQ_ADDRESS', 'localhost:50000'),
        clientId: 'nestjs-module-config-register-async-client',
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}
```

Like the root variants, `registerAsync` also accepts `useClass` / `useExisting` factories — here implementing `KubeMQClientOptionsFactory.createKubeMQClientOptions()`.

## Multi-broker [#multi-broker]

Because each `register` call produces an independent client bound to its own DI token, you can register multiple named clients against **different addresses**. This is the basis for fan-in / fan-out topologies that span more than one broker:

```typescript title="app.module.ts"
import { Module } from '@nestjs/common';
import { KubeMQModule } from '@kubemq/nestjs-transport';
import { MultiBrokerHandlerService } from './multi-broker.handler.js';
import { MultiBrokerService } from './multi-broker.service.js';

const primaryAddress = process.env.KUBEMQ_PRIMARY_ADDRESS ?? 'localhost:50000';
const secondaryAddress = process.env.KUBEMQ_SECONDARY_ADDRESS ?? 'localhost:50000';

@Module({
  imports: [
    KubeMQModule.forRoot({
      address: primaryAddress,
      clientId: 'nestjs-module-config-multi-broker-server',
      isGlobal: true,
    }),
    KubeMQModule.register({
      name: 'PRIMARY_BROKER',
      address: primaryAddress,
      clientId: 'nestjs-module-config-multi-broker-primary',
    }),
    KubeMQModule.register({
      name: 'SECONDARY_BROKER',
      address: secondaryAddress,
      clientId: 'nestjs-module-config-multi-broker-secondary',
    }),
  ],
  providers: [MultiBrokerHandlerService, MultiBrokerService],
})
export class AppModule {}
```

A service then injects each broker by its own token and targets them independently:

```typescript title="multi-broker.service.ts"
import { Injectable, Inject, Logger } from '@nestjs/common';
import { KubeMQClientProxy } from '@kubemq/nestjs-transport';
import { firstValueFrom } from 'rxjs';

@Injectable()
export class MultiBrokerService {
  private readonly logger = new Logger(MultiBrokerService.name);

  constructor(
    @Inject('PRIMARY_BROKER') private readonly primary: KubeMQClientProxy,
    @Inject('SECONDARY_BROKER') private readonly secondary: KubeMQClientProxy,
  ) {}

  async sendToPrimary(): Promise<void> {
    await this.primary.connect();
    await firstValueFrom(
      this.primary.emit('nestjs-module-config.multi-broker-primary', {
        source: 'primary',
        message: 'Hello from primary broker',
      }),
    );
  }

  async sendToSecondary(): Promise<void> {
    await this.secondary.connect();
    await firstValueFrom(
      this.secondary.emit('nestjs-module-config.multi-broker-secondary', {
        source: 'secondary',
        message: 'Hello from secondary broker',
      }),
    );
  }
}
```

The two addresses may point at the same broker with different client identities, or at genuinely separate clusters — the registration is identical either way.

## forFeature / forFeatureAsync [#forfeature--forfeatureasync]

`forFeature` creates a **feature-scoped client** that inherits its connection configuration from `forRoot` and prepends a `channelPrefix` to every `send` / `emit` pattern. It is backed by `ScopedKubeMQClientProxy`, which delegates to the shared client registered by `forRoot` (the `KUBEMQ_SDK_CLIENT` provider) and rewrites the channel name on each call.

```typescript title="orders.module.ts"
import { Module } from '@nestjs/common';
import { KubeMQModule } from '@kubemq/nestjs-transport';
import { OrdersService } from './orders.service.js';

@Module({
  imports: [
    // Connection config inherited from the global forRoot() registration.
    KubeMQModule.forFeature({
      name: 'ORDERS_CLIENT',
      channelPrefix: 'orders.',
    }),
  ],
  providers: [OrdersService],
})
export class OrdersModule {}
```

With the prefix `orders.` in place, a service that emits to `created` actually targets `orders.created`, and `send('get')` targets `orders.get` — the prefix is applied transparently:

```typescript title="orders.service.ts"
import { Injectable, Inject } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
import { firstValueFrom } from 'rxjs';

@Injectable()
export class OrdersService {
  constructor(@Inject('ORDERS_CLIENT') private readonly client: ClientProxy) {}

  async created(orderId: string): Promise<void> {
    // Emits to "orders.created"
    await firstValueFrom(this.client.emit('created', { orderId }));
  }
}
```

`KubeMQFeatureOptions` carries only the scoped concerns — the injection token (`name`) and the optional `channelPrefix`. There is no `address` or `clientId` here, because the connection is reused from `forRoot`. For runtime-resolved prefixes, `forFeatureAsync` accepts the familiar `imports` / `useFactory` / `inject` (and `useClass` / `useExisting`) shape, where the factory returns `KubeMQFeatureOptions`.

<Callout type="info">
  `forFeature` requires a prior `forRoot` (or `forRootAsync`) registration — the scoped proxy injects the shared `KUBEMQ_SDK_CLIENT` and will fail to resolve if no global connection exists.
</Callout>

## Connecting the microservice (server side) [#connecting-the-microservice-server-side]

The module configures DI and outbound clients. It does **not** start the inbound listener. To dispatch incoming messages to your `@CommandHandler` / `@EventHandler` (and the other three) methods, construct a `KubeMQServer` strategy and pass it to `app.connectMicroservice()`, then call `startAllMicroservices()`:

```typescript title="main.ts"
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import { KubeMQServer } from '@kubemq/nestjs-transport';
import { AppModule } from './app.module.js';

async function bootstrap(): Promise<void> {
  const app = await NestFactory.create(AppModule);

  // Inbound: the strategy is constructed separately from the module
  // and dispatches messages to the @*Handler methods.
  app.connectMicroservice({
    strategy: new KubeMQServer({
      address: process.env.KUBEMQ_ADDRESS ?? 'localhost:50000',
      clientId: 'nestjs-module-config-for-root-server',
    }),
  });

  await app.startAllMicroservices();
  await app.listen(3000);
}
bootstrap();
```

Keep the distinction clear:

| Concern                                            | Where it lives                             | Role                                                        |
| -------------------------------------------------- | ------------------------------------------ | ----------------------------------------------------------- |
| `KubeMQModule.forRoot` / `register` / `forFeature` | `app.module.ts` imports                    | DI configuration and outbound clients (`KubeMQClientProxy`) |
| `new KubeMQServer({ ... })`                        | `main.ts`, passed to `connectMicroservice` | Inbound transport strategy that routes messages to handlers |

The same instance you pass to `connectMicroservice` is also what a `KubeMQHealthIndicator` should wrap, so health checks share the transport's connection and subscription error map.

## Common gotchas [#common-gotchas]

<Accordions>
  <Accordion title="Handler decorators do not fire">
    A handler class must be registered as a **provider** in a module that imports `KubeMQModule`. The handler has to be a NestJS-managed provider — not a standalone class that you instantiate yourself — or the framework never discovers its `@*Handler` metadata.
  </Accordion>

  <Accordion title="Messages are sent but never received">
    Confirm the channel name matches exactly between sender and receiver (remember any `forFeature` `channelPrefix`), and make sure the server microservice was actually started by calling `app.startAllMicroservices()`. Without it, no inbound subscriptions are opened and handlers stay silent.
  </Accordion>

  <Accordion title="Scoped client cannot be resolved">
    `forFeature` / `forFeatureAsync` depend on the shared client provided by `forRoot`. Register a global `forRoot` (with `isGlobal: true`) before importing the feature module.
  </Accordion>
</Accordions>

## Related [#related]

<Cards>
  <Card title="Usage" href="/integrations/nestjs/how-to/usage" description="Commands, Queries, Events, Events Store, and Queues with the five handler decorators." />

  <Card title="Configuration reference" href="/integrations/nestjs/reference/configuration" description="The full KubeMQServerOptions / KubeMQClientOptions surface, defaults, and units." />

  <Card title="Getting Started" href="/integrations/nestjs/tutorials/getting-started" description="Stand up a hybrid app, register a handler, and send your first message." />
</Cards>
