@nam088/nestjs-rabbitmq
Version:
A comprehensive RabbitMQ module for NestJS with decorator-based message handling
469 lines (379 loc) • 11.4 kB
Markdown
# /nestjs-rabbitmq
A comprehensive and production-ready RabbitMQ module for NestJS with decorator-based message handling, inspired by modern NestJS patterns.
## Features
**Decorator-Based API** - Use `` to handle messages declaratively
**Multi-Connection Support** - Manage multiple RabbitMQ connections
**Health Checks** - Built-in health indicators for monitoring
**Auto-Discovery** - Automatic message handler registration (configurable scan scope)
**TypeScript First** - Full type safety and IntelliSense support
**Exchange Patterns** - Support for direct, topic, fanout exchanges
**Message Patterns** - Pub/Sub, Request/Reply, Work Queues
**Error Handling** - Built-in retry logic and dead letter queues
**Well Tested** - Comprehensive test coverage
## Installation
```bash
npm install /nestjs-rabbitmq amqplib amqp-connection-manager
# or
yarn add /nestjs-rabbitmq amqplib amqp-connection-manager
# or
pnpm add /nestjs-rabbitmq amqplib amqp-connection-manager
```
> Note: `amqplib` and `amqp-connection-manager` are peer dependencies. Install them in your application.
## Quick Start
### 1. Import the Module
```typescript
import { Module } from '@nestjs/common';
import { RabbitMQModule } from '@nam088/nestjs-rabbitmq';
export class AppModule {}
```
### 2. Create a Message Handler
```typescript
import { Injectable } from '@nestjs/common';
import { RabbitSubscribe } from '@nam088/nestjs-rabbitmq';
export class UserService {
async handleUserCreated(message: any) {
console.log('User created:', message);
}
}
```
### 3. Publish Messages
```typescript
import { Injectable } from '@nestjs/common';
import { InjectRabbitMQ, RabbitMQService } from '@nam088/nestjs-rabbitmq';
export class NotificationService {
constructor(
private readonly rabbitmq: RabbitMQService,
) {}
async notifyUserCreated(userId: string) {
await this.rabbitmq.publish('users', 'user.created', {
userId,
timestamp: new Date(),
});
}
}
```
## Configuration
### Basic Configuration
```typescript
RabbitMQModule.forRoot({
uri: 'amqp://localhost:5672',
connectionName: 'default',
})
```
### Advanced Configuration
```typescript
RabbitMQModule.forRoot({
uri: 'amqp://localhost:5672',
connectionName: 'default',
exchanges: [
{
name: 'users',
type: 'topic',
options: { durable: true },
},
{
name: 'orders',
type: 'direct',
options: { durable: true },
},
],
connectionOptions: {
heartbeatIntervalInSeconds: 5,
reconnectTimeInSeconds: 10,
},
})
```
### Async Configuration
```typescript
RabbitMQModule.forRootAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
uri: config.get('RABBITMQ_URI'),
connectionName: 'default',
}),
inject: [ConfigService],
})
```
### Multiple Connections
```typescript
export class AppModule {}
// Inject specific connection
export class MyService {
constructor(
private readonly primary: RabbitMQService,
private readonly secondary: RabbitMQService,
) {}
}
```
## Message Patterns
### Pub/Sub Pattern
```typescript
// Publisher
await rabbitmq.publish('events', 'user.updated', { userId: 123 });
// Subscriber
async handleUserEvents(message: any) {
// Handle all user events
}
```
### Work Queue Pattern
```typescript
// Multiple workers sharing the same queue
async processTask(task: any) {
// Only one worker will process each task
}
```
### RPC Pattern
**Note**: For RPC pattern, use `` decorator. The queue will be automatically declared when the handler is registered.
```typescript
// Request
const result = await rabbitmq.request('calculator-rpc', { a: 10, b: 5, op: 'add' });
// Reply Handler
async handleRPC( data: { a: number; b: number; op: string }) {
const result = performCalculation(data);
return result; // Automatically sends reply back to requestor
}
```
#### Minimal example (add two numbers)
```typescript
// Handler
add( payload: { a: number; b: number }) {
return { ok: true, sum: Number(payload?.a ?? 0) + Number(payload?.b ?? 0) };
}
// Client
const res = await rabbitmq.request('math.add', { a: 5, b: 7 });
// -> { ok: true, sum: 12 }
```
## Advanced Decorators
### RPC Handler with
```typescript
import { Injectable } from '@nestjs/common';
import { RabbitRPC, RabbitPayload } from '@nam088/nestjs-rabbitmq';
export class CalculatorService {
async calculate( data: { a: number; b: number; op: string }) {
switch (data.op) {
case 'add':
return data.a + data.b;
case 'multiply':
return data.a * data.b;
default:
throw new Error('Unknown operation');
}
}
}
```
### Message Handler with
```typescript
import { Injectable } from '@nestjs/common';
import { RabbitHandler, RabbitPayload, RabbitMessage } from '@nam088/nestjs-rabbitmq';
export class OrderService {
async handleOrderCreated(
order: { id: string; amount: number },
properties: any,
) {
console.log('Order ID:', order.id);
console.log('Message ID:', properties.messageId);
// Process order
await this.processOrder(order);
}
}
```
### Parameter Decorators
```typescript
import { Injectable } from '@nestjs/common';
import {
RabbitSubscribe,
RabbitPayload,
RabbitMessage,
RabbitContext
} from '@nam088/nestjs-rabbitmq';
export class MessageProcessor {
// Extract entire payload
async handleUser( user: { id: string; name: string }) {
console.log('User:', user);
}
// Extract specific field from payload
async handleNotification(
userId: string,
message: string,
) {
console.log(`Send ${message} to user ${userId}`);
}
// Access full message context
async handleLog(
data: any,
fullMessage: any,
) {
console.log('Routing Key:', fullMessage.fields.routingKey);
console.log('Exchange:', fullMessage.fields.exchange);
console.log('Data:', data);
}
// Get message properties
async handleTask(
task: any,
props: any,
) {
console.log('Correlation ID:', props.correlationId);
console.log('Timestamp:', props.timestamp);
}
}
```
## Discovery & Performance
For large applications, you can limit scanning scope to speed up bootstrap and avoid scanning the whole app.
### Options (RabbitMQModuleOptions)
```typescript
RabbitMQModule.forRoot({
uri: 'amqp://localhost:5672',
// Disable discovery entirely (manual registration only)
// autoDiscover: false,
// Limit what to scan
scanScope: 'all', // 'all' | 'modules' | 'providers' | 'annotated'
// If using 'modules', specify which modules to scan
// includeModules: [AppModule, 'PaymentsModule'],
// If using 'providers', specify which providers to scan (and/or exclude)
// includeProviders: [ConsumerService, 'BillingConsumerService'],
// excludeProviders: ['SomeHeavyProvider'],
});
```
### Annotated-only scanning
Use the `` class decorator to mark consumer classes. When `scanScope: 'annotated'` is set, only these classes are scanned.
```typescript
import { RabbitController, RabbitSubscribe, RabbitRPC } from '@nam088/nestjs-rabbitmq';
export class ConsumerService {
onMessage(msg: any) {}
onRpc( data: any) { return { ok: true, data }; }
}
```
### Multi-Connection with Decorators
```typescript
export class MultiConnService {
async handlePrimary( data: any) {
return { status: 'processed', data };
}
async handleSecondary( message: any) {
console.log('From secondary connection:', message);
}
}
```
## Health Checks
```typescript
import { Module } from '@nestjs/common';
import { TerminusModule } from '@nestjs/terminus';
import { RabbitMQHealthIndicator } from '@nam088/nestjs-rabbitmq';
export class HealthModule {}
export class HealthController {
constructor(
private health: HealthCheckService,
private rabbitmq: RabbitMQHealthIndicator,
) {}
check() {
return this.health.check([
() => this.rabbitmq.isHealthy('default'),
]);
}
}
```
## API Reference
### RabbitMQModule
- `forRoot(options)` - Register with static configuration
- `forRootAsync(options)` - Register with async configuration
### RabbitMQService
- `publish(exchange, routingKey, message, options?)` - Publish a message
- `sendToQueue(queue, message, options?)` - Send to queue directly
- `request(queue, message, options?)` - RPC request-reply
- `createChannel()` - Get the underlying channel
- `getConnection()` - Get the connection manager
### Decorators
- `` - Subscribe to messages
- `` - Inject RabbitMQ service
- `` - Mark method as RPC handler (request-reply pattern)
- `` - Generic message handler decorator
- `` - Extract payload from message
- `` / `` - Get full message context
- `` - Mark class for annotated-only discovery
## Examples
Check out the [examples](./examples) directory for complete working examples:
- [Basic Use](./examples/basic-use) - Simple pub/sub and RPC (includes math.add)
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
MIT © Nam088