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';
}
}