@nam088/nestjs-rabbitmq
Version:
A comprehensive RabbitMQ module for NestJS with decorator-based message handling
216 lines • 8.1 kB
JavaScript
"use strict";
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 __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var RabbitMQService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RabbitMQService = void 0;
const crypto_1 = require("crypto");
const common_1 = require("@nestjs/common");
let RabbitMQService = RabbitMQService_1 = class RabbitMQService {
constructor(connectionManager, connectionName, logLevel = 'error') {
this.connectionManager = connectionManager;
this.connectionName = connectionName;
this.logger = new common_1.Logger(RabbitMQService_1.name);
this.rpcQueues = new Map();
this.logLevel = logLevel;
}
async onModuleDestroy() {
await this.close();
}
getChannel() {
return this.channel;
}
getConnectionManager() {
return this.connectionManager;
}
async getReplyQueue() {
const replyQueue = 'amq.rabbitmq.reply-to';
await this.channel.consume(replyQueue, (message) => {
if (!message)
return;
const { correlationId } = message.properties;
const callbacks = this.rpcQueues.get(replyQueue);
if (!callbacks)
return;
if (!callbacks.has(correlationId))
return;
const callback = callbacks.get(correlationId);
const response = this.deserializeMessage(message.content);
callback(response);
callbacks.delete(correlationId);
}, { noAck: true });
return replyQueue;
}
async publish(exchange, routingKey, message, options) {
try {
const content = this.serializeMessage(message);
const publishOptions = {
persistent: true,
...options,
};
await this.channel.publish(exchange, routingKey, content, publishOptions);
return true;
}
catch (error) {
this.logger.error(`Failed to publish message to ${exchange}/${routingKey}`, error.stack);
throw error;
}
}
deserializeMessage(buffer) {
try {
const str = buffer.toString();
return JSON.parse(str);
}
catch {
return buffer.toString();
}
}
serializeMessage(message) {
if (Buffer.isBuffer(message)) {
return message;
}
if (typeof message === 'string') {
return Buffer.from(message);
}
return Buffer.from(JSON.stringify(message));
}
async assertExchange(exchange, type, options) {
await this.channel.assertExchange(exchange, type, {
durable: true,
...options,
});
}
async assertQueue(queue, options) {
await this.channel.assertQueue(queue, {
durable: true,
...options,
});
}
async bindQueue(queue, exchange, routingKey) {
await this.channel.bindQueue(queue, exchange, routingKey);
}
async close() {
this.info(`Closing RabbitMQ connection: ${this.connectionName}`);
await this.channel?.close();
await this.connectionManager?.close();
}
async consume(queue, onMessage, options) {
await this.channel.consume(queue, async (message) => {
if (!message) {
return;
}
try {
const content = this.deserializeMessage(message.content);
await onMessage(content);
this.channel.ack(message);
}
catch (error) {
this.logger.error(`Error processing message from ${queue}`, error.stack);
this.channel.nack(message, false, false);
}
}, {
noAck: false,
...options,
});
this.info(`Started consuming from queue: ${queue}`);
}
async initialize() {
this.info(`Initializing RabbitMQ channel for connection: ${this.connectionName}`);
this.channel = this.connectionManager.createChannel({
json: false,
setup: async (channel) => {
await channel.prefetch(1);
this.info(`Channel setup complete for: ${this.connectionName}`);
},
});
await this.channel.waitForConnect();
this.info(`RabbitMQ channel connected for: ${this.connectionName}`);
}
isConnected() {
return this.connectionManager.isConnected();
}
async request(queue, message, options = {}) {
const { publishOptions = {}, timeout = 30000 } = options;
const correlationId = (0, crypto_1.randomUUID)();
const replyQueue = await this.getReplyQueue();
return new Promise(async (resolve, reject) => {
const timeoutId = setTimeout(() => {
const callbacks = this.rpcQueues.get(replyQueue);
this.logger.error(`RPC timeout after ${timeout}ms (correlationId=${correlationId})`);
callbacks?.delete(correlationId);
reject(new Error(`RPC timeout after ${timeout}ms`));
}, timeout);
if (!this.rpcQueues.has(replyQueue)) {
this.rpcQueues.set(replyQueue, new Map());
}
const callbackMap = this.rpcQueues.get(replyQueue);
callbackMap.set(correlationId, (response) => {
clearTimeout(timeoutId);
resolve(response);
});
try {
await this.sendToQueue(queue, message, {
...publishOptions,
replyTo: replyQueue,
correlationId,
});
}
catch (error) {
clearTimeout(timeoutId);
callbackMap.delete(correlationId);
this.logger.error(`[RPC] Send failed: correlationId=${correlationId}, queue=${queue}, error=${String(error?.message ?? error)}`, error?.stack);
reject(error instanceof Error ? error : new Error(String(error)));
}
});
}
async sendToQueue(queue, message, options) {
try {
const content = this.serializeMessage(message);
const sendOptions = {
persistent: true,
...options,
};
await this.channel.sendToQueue(queue, content, sendOptions);
return true;
}
catch (error) {
this.logger.error(`Failed to send message to queue ${queue}`, error.stack);
throw error;
}
}
debug(message) {
if (this.shouldLog('debug'))
this.logger.debug(message);
}
info(message) {
if (this.shouldLog('log'))
this.logger.log(message);
}
shouldLog(level) {
const order = {
debug: 3,
error: 0,
log: 2,
none: -1,
warn: 1,
};
return order[level] <= order[this.logLevel];
}
warn(message) {
if (this.shouldLog('warn'))
this.logger.warn(message);
}
};
exports.RabbitMQService = RabbitMQService;
exports.RabbitMQService = RabbitMQService = RabbitMQService_1 = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [Object, String, String])
], RabbitMQService);
//# sourceMappingURL=rabbitmq.service.js.map