Skip to main content

RabbitMQ Module

The @opra/nestjs-rabbitmq package integrates the OPRA RabbitMQ adapter into a NestJS application. It discovers your OPRA message queue controllers automatically from the NestJS provider tree, builds the ApiDocument, wires up the AMQP consumer lifecycle through NestJS hooks, and resolves class-based interceptors through the NestJS DI container — all transparently.


Installation​

npm install @opra/nestjs-rabbitmq @opra/rabbitmq @opra/common

Setup​

Import OpraRabbitmqModule in your root application module:

import { Module } from '@nestjs/common';
import { OpraRabbitmqModule } from '@opra/nestjs-rabbitmq';
import { OrdersController } from './orders/orders.controller.js';
import { OrdersService } from './orders/orders.service.js';
import * as models from './models/models.js';

@Module({
imports: [
OpraRabbitmqModule.forRoot({
providers: [OrdersController, OrdersService],
name: 'OrderApi',
info: { title: 'Order API', version: '1.0' },
types: [...Object.values(models)],
connection: 'amqp://guest:guest@localhost:5672',
}),
],
})
export class AppModule {}

Controllers decorated with @MQController are discovered automatically from the NestJS provider tree — no explicit controllers list is needed in the module options.

Options​

OptionTypeDescription
namestringAPI name.
infoobjectDocument metadata — title, version, description.
typesany[]Data types to register (decorated classes, EnumType results).
referencesRecord<string, ReferenceThunk>Namespaced references to other ApiDocument instances or async thunks.
connectionstring | string[] | ConnectionOptionsAMQP connection URL(s) or a ConnectionOptions object.
defaults{ consumer?: ConsumerConfig }Default consumer options applied to all operations unless overridden.
scopestringValidation scope applied during message decoding.
interceptors(InterceptorFunction | IRabbitmqInterceptor | Type<IRabbitmqInterceptor>)[]Interceptor chain executed on every message. Class types are resolved through the NestJS DI container.
loggerLoggerCustom NestJS logger instance. Defaults to a logger named after the API.
logExtrabooleanLog additional diagnostic output from the AMQP client.
importsany[]NestJS modules to import into the OPRA module context.
providersProvider[]NestJS providers available for injection inside controllers.
exportsany[]Providers to export from the OPRA module to the rest of the application.
globalbooleanRegister the module as a NestJS global module.

Async configuration​

Use forRootAsync() when connection, info, types, or other options depend on injected services:

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { OpraRabbitmqModule } from '@opra/nestjs-rabbitmq';
import * as models from './models/models.js';

@Module({
imports: [
ConfigModule.forRoot(),
OpraRabbitmqModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
providers: [OrdersController, OrdersService],
useFactory: (config: ConfigService) => ({
name: 'OrderApi',
info: {
title: 'Order API',
version: config.get('API_VERSION'),
},
types: [...Object.values(models)],
connection: config.get('RABBITMQ_URL'),
}),
}),
],
})
export class AppModule {}

How it works​

OpraRabbitmqModule scans all NestJS providers in its module context for classes decorated with @MQController and builds the ApiDocument from them automatically. No explicit controller list is required.

Once the document is ready, the module creates a RabbitmqAdapter and manages its lifecycle through NestJS hooks:

  • onApplicationBootstrap — calls adapter.start(), which establishes the AMQP connection and begins consuming from all declared queues.
  • onApplicationShutdown — calls adapter.close() to gracefully shut down all consumers and the connection.

Dependency injection in controllers​

OPRA MQ controllers are registered as NestJS providers, so you can inject services directly into them:

import { MQController, MQOperation } from '@opra/common';
import { RabbitmqContext } from '@opra/rabbitmq';
import { Injectable } from '@nestjs/common';
import { OrdersService } from './orders.service.js';

@Injectable()
@MQController()
export class OrdersController {
constructor(private readonly service: OrdersService) {}

@MQOperation({ channel: 'orders.created', type: OrderPayload })
async onOrderCreated(ctx: RabbitmqContext) {
await this.service.process(ctx.content);
}
}

Register the controller as a NestJS provider in the module options:

OpraRabbitmqModule.forRoot({
providers: [OrdersController, OrdersService],
// ...
})

Class-based interceptors​

Class-based interceptors passed to the interceptors option are resolved through the NestJS DI container, so they can have injected dependencies:

import { IRabbitmqInterceptor, RabbitmqContext } from '@opra/rabbitmq';
import { Injectable } from '@nestjs/common';
import { MetricsService } from './metrics.service.js';

@Injectable()
class MetricsInterceptor implements IRabbitmqInterceptor {
constructor(private readonly metrics: MetricsService) {}

async intercept(ctx: RabbitmqContext, next: () => Promise<any>) {
const start = Date.now();
await next();
this.metrics.record(ctx.queue, Date.now() - start);
}
}

OpraRabbitmqModule.forRoot({
providers: [OrdersController, OrdersService, MetricsService, MetricsInterceptor],
interceptors: [MetricsInterceptor], // resolved via DI
// ...
})

Injecting the adapter​

The RabbitmqAdapter instance is exported from the module and can be injected elsewhere in your application:

import { Injectable } from '@nestjs/common';
import { RabbitmqAdapter } from '@opra/rabbitmq';

@Injectable()
export class HealthService {
constructor(private readonly adapter: RabbitmqAdapter) {}

isReady() {
return this.adapter.status === 'started';
}
}