@nam088/nestjs-rabbitmq
Version:
A comprehensive RabbitMQ module for NestJS with decorator-based message handling
338 lines • 16.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var RabbitMQCoreModule_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RabbitMQCoreModule = void 0;
const core_1 = require("@nestjs/core");
const common_1 = require("@nestjs/common");
const metadata_scanner_1 = require("@nestjs/core/metadata-scanner");
const amqp = __importStar(require("amqp-connection-manager"));
const rabbitmq_service_1 = require("./services/rabbitmq.service");
const service_discovery_service_1 = require("./services/service-discovery.service");
const rabbit_controller_decorator_1 = require("./decorators/rabbit-controller.decorator");
const constants_1 = require("./constants");
let RabbitMQCoreModule = RabbitMQCoreModule_1 = class RabbitMQCoreModule {
constructor(moduleRef, modulesContainer) {
this.moduleRef = moduleRef;
this.modulesContainer = modulesContainer;
this.logger = new common_1.Logger(RabbitMQCoreModule_1.name);
this.metadataScanner = new metadata_scanner_1.MetadataScanner();
}
static forRoot(options) {
const connectionName = options.connectionName || constants_1.DEFAULT_CONNECTION_NAME;
const connectionManagerProvider = {
provide: `${constants_1.RABBITMQ_CONNECTION_MANAGER}_${connectionName}`,
useFactory: () => amqp.connect([options.uri], {
heartbeatIntervalInSeconds: options.connectionOptions?.heartbeatIntervalInSeconds || 5,
reconnectTimeInSeconds: options.connectionOptions?.reconnectTimeInSeconds || 10,
}),
};
const serviceProvider = {
inject: [`${constants_1.RABBITMQ_CONNECTION_MANAGER}_${connectionName}`],
provide: `${constants_1.RABBITMQ_SERVICE}_${connectionName}`,
useFactory: async (connectionManager) => {
const service = new rabbitmq_service_1.RabbitMQService(connectionManager, connectionName, options.logLevel ?? 'error');
await service.initialize();
if (options.exchanges) {
for (const exchange of options.exchanges) {
await service.assertExchange(exchange.name, exchange.type, exchange.options);
}
}
if (options.queues) {
for (const queue of options.queues) {
await service.assertQueue(queue.name, queue.options);
}
}
return service;
},
};
const optionsProvider = {
provide: `${constants_1.RABBITMQ_MODULE_OPTIONS}_${connectionName}`,
useValue: options,
};
const providers = [connectionManagerProvider, serviceProvider, optionsProvider];
const exports = [serviceProvider];
if (options.serviceDiscovery?.enabled) {
const discoveryProvider = {
inject: [`${constants_1.RABBITMQ_SERVICE}_${connectionName}`],
provide: `${constants_1.RABBITMQ_SERVICE_DISCOVERY}_${connectionName}`,
useFactory: (rabbitService) => new service_discovery_service_1.ServiceDiscoveryService(rabbitService, options.serviceDiscovery),
};
providers.push(discoveryProvider);
exports.push(discoveryProvider);
}
return {
providers,
exports,
module: RabbitMQCoreModule_1,
};
}
static forRootAsync(options) {
const connectionName = options.connectionName || constants_1.DEFAULT_CONNECTION_NAME;
const connectionManagerProvider = {
inject: [`${constants_1.RABBITMQ_MODULE_OPTIONS}_${connectionName}`],
provide: `${constants_1.RABBITMQ_CONNECTION_MANAGER}_${connectionName}`,
useFactory: (moduleOptions) => amqp.connect([moduleOptions.uri], {
heartbeatIntervalInSeconds: moduleOptions.connectionOptions?.heartbeatIntervalInSeconds || 5,
reconnectTimeInSeconds: moduleOptions.connectionOptions?.reconnectTimeInSeconds || 10,
}),
};
const serviceProvider = {
inject: [
`${constants_1.RABBITMQ_CONNECTION_MANAGER}_${connectionName}`,
`${constants_1.RABBITMQ_MODULE_OPTIONS}_${connectionName}`,
],
provide: `${constants_1.RABBITMQ_SERVICE}_${connectionName}`,
useFactory: async (connectionManager, moduleOptions) => {
const service = new rabbitmq_service_1.RabbitMQService(connectionManager, connectionName, moduleOptions.logLevel ?? 'error');
await service.initialize();
if (moduleOptions.exchanges) {
for (const exchange of moduleOptions.exchanges) {
await service.assertExchange(exchange.name, exchange.type, exchange.options);
}
}
if (moduleOptions.queues) {
for (const queue of moduleOptions.queues) {
await service.assertQueue(queue.name, queue.options);
}
}
return service;
},
};
const asyncProviders = this.createAsyncProviders(options, connectionName);
const providers = [...asyncProviders, connectionManagerProvider, serviceProvider];
const exports = [serviceProvider];
return {
imports: options.imports || [],
providers,
exports,
module: RabbitMQCoreModule_1,
};
}
async onApplicationBootstrap() {
const moduleOptions = this.moduleRef.get(`${constants_1.RABBITMQ_MODULE_OPTIONS}_${constants_1.DEFAULT_CONNECTION_NAME}`, { strict: false });
const autoDiscover = moduleOptions?.autoDiscover !== false;
if (!autoDiscover) {
return;
}
for (const moduleRef of this.modulesContainer.values()) {
if (!this.shouldScanModule(moduleRef, moduleOptions))
continue;
await this.processModuleProviders(moduleRef, moduleOptions);
}
}
static createAsyncOptionsProvider(options, connectionName) {
if (options.useFactory) {
return {
inject: options.inject || [],
provide: `${constants_1.RABBITMQ_MODULE_OPTIONS}_${connectionName}`,
useFactory: options.useFactory,
};
}
const inject = options.useExisting || options.useClass;
return {
inject: inject ? [inject] : [],
provide: `${constants_1.RABBITMQ_MODULE_OPTIONS}_${connectionName}`,
useFactory: async (optionsFactory) => await optionsFactory.createRabbitMQOptions(),
};
}
static createAsyncProviders(options, connectionName) {
if (options.useExisting || options.useFactory) {
return [this.createAsyncOptionsProvider(options, connectionName)];
}
if (options.useClass) {
return [
this.createAsyncOptionsProvider(options, connectionName),
{
provide: options.useClass,
useClass: options.useClass,
},
];
}
return [];
}
async processModuleProviders(moduleRef, options) {
for (const provider of moduleRef.providers.values()) {
if (!this.shouldScanProvider(provider, options))
continue;
const instance = provider.instance;
const prototype = instance && Object.getPrototypeOf(instance);
if (!instance || !prototype) {
continue;
}
this.processProviderMethods(instance, prototype);
}
}
processProviderMethods(instance, prototype) {
const SUBSCRIBE_KEY = 'RABBITMQ_SUBSCRIBE_METADATA';
const RPC_KEY = 'RABBIT_RPC_METADATA';
const methodNames = this.metadataScanner.getAllMethodNames(prototype);
for (const methodName of methodNames) {
const methodDescriptor = prototype[methodName];
const subOptions = methodDescriptor ? Reflect.getMetadata(SUBSCRIBE_KEY, methodDescriptor) : null;
const rpcOptions = methodDescriptor ? Reflect.getMetadata(RPC_KEY, methodDescriptor) : null;
if (subOptions)
void this.registerDiscoveredHandler(instance, methodName, subOptions);
if (rpcOptions)
void this.registerDiscoveredRpcHandler(instance, methodName, rpcOptions);
}
}
async registerDiscoveredHandler(instance, methodName, options) {
const connectionName = options.connectionName || constants_1.DEFAULT_CONNECTION_NAME;
const rabbitService = this.moduleRef.get(`${constants_1.RABBITMQ_SERVICE}_${connectionName}`, {
strict: false,
});
if (!rabbitService) {
this.logger.error(`RabbitMQ service not found for connection: ${connectionName}`);
return;
}
try {
if (options.queue) {
await rabbitService.assertQueue(options.queue, options.queueOptions);
}
if (options.exchange && options.routingKey && options.queue) {
await rabbitService.bindQueue(options.queue, options.exchange, options.routingKey);
}
const handler = instance[methodName].bind(instance);
if (options.queue) {
await rabbitService.consume(options.queue, async (message) => handler(message), options.consumeOptions);
this.logger.log(`Registered subscriber: ${instance.constructor?.name}.${methodName} -> ${options.queue}`);
}
}
catch (error) {
this.logger.error(`Failed to register subscriber ${(instance.constructor && instance.constructor.name) || 'Unknown'}.${methodName}`, error?.stack);
}
}
async registerDiscoveredRpcHandler(instance, methodName, options) {
const connectionName = options.connectionName || constants_1.DEFAULT_CONNECTION_NAME;
const rabbitService = this.moduleRef.get(`${constants_1.RABBITMQ_SERVICE}_${connectionName}`, {
strict: false,
});
if (!rabbitService) {
this.logger.error(`RabbitMQ service not found for connection: ${connectionName}`);
return;
}
if (options.queue) {
await rabbitService.assertQueue(options.queue, options.queueOptions);
}
const handler = instance[methodName].bind(instance);
const channel = rabbitService.getChannel();
await channel.consume(options.queue, async (msg) => {
if (!msg) {
return;
}
try {
const payloadStr = msg.content?.toString();
let payload = payloadStr;
try {
payload = JSON.parse(payloadStr);
}
catch {
this.logger.error(`Failed to parse payload as JSON, using raw string: ${payloadStr}`);
}
const response = await handler(payload);
const responseBuffer = Buffer.isBuffer(response)
? response
: Buffer.from(JSON.stringify(response ?? null));
if (msg.properties?.replyTo) {
await channel.sendToQueue(msg.properties.replyTo, responseBuffer, {
persistent: false,
correlationId: msg.properties.correlationId,
});
}
channel.ack?.(msg);
}
catch (error) {
this.logger.error(`Failed to process RPC ${(instance.constructor && instance.constructor.name) || 'Unknown'}.${methodName}`, error?.stack);
channel.nack?.(msg, false, false);
}
}, {
noAck: false,
prefetch: options.prefetchCount || 1,
...options.consumeOptions,
});
this.logger.log(`Registered RPC handler: ${instance.constructor?.name}.${methodName} -> ${options.queue}`);
}
shouldScanModule(moduleRef, options) {
const scope = options?.scanScope ?? 'all';
if (scope === 'all')
return true;
if (scope === 'modules' || scope === 'annotated' || scope === 'providers') {
const include = options?.includeModules;
if (!include || include.length === 0)
return true;
const name = moduleRef?.metatype?.name;
return include.some((m) => (typeof m === 'string' ? m === name : m === moduleRef?.metatype));
}
return true;
}
shouldScanProvider(provider, options) {
const scope = options?.scanScope ?? 'all';
const token = provider?.name || provider?.metatype?.name || provider?.token;
if (options?.excludeProviders && options.excludeProviders.length > 0) {
if (options.excludeProviders.some((p) => typeof p === 'string' ? p === token : p === provider?.metatype)) {
return false;
}
}
if (options?.includeProviders && options.includeProviders.length > 0) {
const included = options.includeProviders.some((p) => typeof p === 'string' ? p === token : p === provider?.metatype);
if (!included)
return false;
}
if (scope === 'annotated') {
const instance = provider.instance;
const ctor = instance?.constructor ?? provider?.metatype;
return !!(ctor && Reflect.getMetadata && Reflect.getMetadata(rabbit_controller_decorator_1.RABBIT_CONTROLLER_KEY, ctor));
}
return true;
}
};
exports.RabbitMQCoreModule = RabbitMQCoreModule;
exports.RabbitMQCoreModule = RabbitMQCoreModule = RabbitMQCoreModule_1 = __decorate([
(0, common_1.Global)(),
(0, common_1.Module)({}),
__metadata("design:paramtypes", [core_1.ModuleRef,
core_1.ModulesContainer])
], RabbitMQCoreModule);
//# sourceMappingURL=rabbitmq-core.module.js.map