or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-proxies.mdcontext-objects.mdexceptions.mdgrpc.mdindex.mdmessage-patterns.mdmodules.mdtransports.md

modules.mddocs/

0

# Module Integration

1

2

NestJS module integration for dependency injection and configuration management of microservice clients and servers, providing seamless integration with the NestJS dependency injection system and modular architecture.

3

4

## Capabilities

5

6

### Clients Module

7

8

Module for registering and managing microservice client instances with dependency injection support.

9

10

```typescript { .api }

11

/**

12

* Module for microservice client registration and dependency injection

13

*/

14

class ClientsModule {

15

/**

16

* Register clients synchronously with static configuration

17

* @param options - Array of client configuration options

18

* @returns Dynamic module with registered clients

19

*/

20

static register(options: ClientsModuleOptions): DynamicModule;

21

22

/**

23

* Register clients asynchronously with dynamic configuration

24

* @param options - Async configuration options

25

* @returns Dynamic module with registered clients

26

*/

27

static registerAsync(options: ClientsModuleAsyncOptions): DynamicModule;

28

}

29

30

/**

31

* Client configuration options for multiple clients

32

*/

33

interface ClientsModuleOptions extends Array<ClientsModuleOption> {}

34

35

interface ClientsModuleOption {

36

/** Unique name for the client */

37

name: string | symbol;

38

/** Transport configuration */

39

transport: Transport;

40

/** Transport-specific options */

41

options?: any;

42

}

43

44

/**

45

* Async configuration options for clients module

46

*/

47

interface ClientsModuleAsyncOptions {

48

/** Module imports for dependencies */

49

imports?: any[];

50

/** Factory function for creating client options */

51

useFactory?: (...args: any[]) => ClientsModuleOptions | Promise<ClientsModuleOptions>;

52

/** Dependencies to inject into useFactory */

53

inject?: any[];

54

/** Use existing provider for configuration */

55

useExisting?: any;

56

/** Use class provider for configuration */

57

useClass?: any;

58

}

59

```

60

61

**Usage Examples:**

62

63

```typescript

64

import { Module } from '@nestjs/common';

65

import { ClientsModule, Transport } from '@nestjs/microservices';

66

67

// Static registration

68

@Module({

69

imports: [

70

ClientsModule.register([

71

{

72

name: 'USER_SERVICE',

73

transport: Transport.TCP,

74

options: {

75

host: '127.0.0.1',

76

port: 3001,

77

},

78

},

79

{

80

name: 'NOTIFICATION_SERVICE',

81

transport: Transport.REDIS,

82

options: {

83

host: 'localhost',

84

port: 6379,

85

},

86

},

87

{

88

name: 'ANALYTICS_SERVICE',

89

transport: Transport.KAFKA,

90

options: {

91

client: {

92

clientId: 'analytics-client',

93

brokers: ['localhost:9092'],

94

},

95

},

96

},

97

]),

98

],

99

providers: [AppService],

100

controllers: [AppController],

101

})

102

export class AppModule {}

103

104

// Async registration with configuration service

105

@Module({

106

imports: [

107

ConfigModule,

108

ClientsModule.registerAsync({

109

imports: [ConfigModule],

110

useFactory: async (configService: ConfigService) => [

111

{

112

name: 'USER_SERVICE',

113

transport: Transport.TCP,

114

options: {

115

host: configService.get('USER_SERVICE_HOST'),

116

port: configService.get('USER_SERVICE_PORT'),

117

},

118

},

119

{

120

name: 'NOTIFICATION_SERVICE',

121

transport: Transport.REDIS,

122

options: {

123

host: configService.get('REDIS_HOST'),

124

port: configService.get('REDIS_PORT'),

125

password: configService.get('REDIS_PASSWORD'),

126

},

127

},

128

],

129

inject: [ConfigService],

130

}),

131

],

132

})

133

export class AppModule {}

134

```

135

136

### Client Decorator

137

138

Property decorator for injecting microservice client instances into classes.

139

140

```typescript { .api }

141

/**

142

* Property decorator for injecting ClientProxy instances

143

* @param metadata - Client configuration or token name

144

* @returns Property decorator

145

*/

146

function Client(metadata?: ClientOptions | string | symbol): PropertyDecorator;

147

```

148

149

**Usage Examples:**

150

151

```typescript

152

import { Injectable } from '@nestjs/common';

153

import { Client, ClientProxy, Transport } from '@nestjs/microservices';

154

155

@Injectable()

156

export class AppService {

157

// Direct client configuration

158

@Client({

159

transport: Transport.TCP,

160

options: {

161

host: '127.0.0.1',

162

port: 3001,

163

},

164

})

165

private userClient: ClientProxy;

166

167

// Using registered client by name

168

@Client('NOTIFICATION_SERVICE')

169

private notificationClient: ClientProxy;

170

171

@Client('ANALYTICS_SERVICE')

172

private analyticsClient: ClientProxy;

173

174

async getUser(id: number): Promise<any> {

175

return this.userClient.send({ cmd: 'get_user' }, { id }).toPromise();

176

}

177

178

async sendNotification(notification: any): Promise<void> {

179

this.notificationClient.emit('send_notification', notification);

180

}

181

182

async trackEvent(event: any): Promise<void> {

183

this.analyticsClient.emit('track_event', event);

184

}

185

186

// Lifecycle management

187

async onModuleInit() {

188

await this.userClient.connect();

189

await this.notificationClient.connect();

190

await this.analyticsClient.connect();

191

}

192

193

async onModuleDestroy() {

194

await this.userClient.close();

195

await this.notificationClient.close();

196

await this.analyticsClient.close();

197

}

198

}

199

```

200

201

### Service Integration Patterns

202

203

Advanced patterns for integrating microservice clients with NestJS services.

204

205

**Service with Multiple Clients:**

206

207

```typescript

208

import { Injectable, Inject, OnModuleInit, OnModuleDestroy } from '@nestjs/common';

209

import { ClientProxy } from '@nestjs/microservices';

210

211

@Injectable()

212

export class OrderService implements OnModuleInit, OnModuleDestroy {

213

constructor(

214

@Inject('USER_SERVICE') private userClient: ClientProxy,

215

@Inject('INVENTORY_SERVICE') private inventoryClient: ClientProxy,

216

@Inject('PAYMENT_SERVICE') private paymentClient: ClientProxy,

217

@Inject('NOTIFICATION_SERVICE') private notificationClient: ClientProxy,

218

) {}

219

220

async onModuleInit() {

221

// Connect all clients

222

await Promise.all([

223

this.userClient.connect(),

224

this.inventoryClient.connect(),

225

this.paymentClient.connect(),

226

this.notificationClient.connect(),

227

]);

228

}

229

230

async onModuleDestroy() {

231

// Close all clients

232

await Promise.all([

233

this.userClient.close(),

234

this.inventoryClient.close(),

235

this.paymentClient.close(),

236

this.notificationClient.close(),

237

]);

238

}

239

240

async processOrder(orderData: CreateOrderDto): Promise<Order> {

241

try {

242

// Validate user

243

const user = await this.userClient

244

.send({ cmd: 'get_user' }, { id: orderData.userId })

245

.toPromise();

246

247

if (!user) {

248

throw new Error('User not found');

249

}

250

251

// Check inventory

252

const inventoryCheck = await this.inventoryClient

253

.send({ cmd: 'check_availability' }, { items: orderData.items })

254

.toPromise();

255

256

if (!inventoryCheck.available) {

257

throw new Error('Items not available');

258

}

259

260

// Process payment

261

const payment = await this.paymentClient

262

.send({ cmd: 'process_payment' }, {

263

userId: orderData.userId,

264

amount: orderData.total,

265

paymentMethod: orderData.paymentMethod,

266

})

267

.toPromise();

268

269

if (!payment.success) {

270

throw new Error('Payment failed');

271

}

272

273

// Reserve inventory

274

await this.inventoryClient

275

.send({ cmd: 'reserve_items' }, { items: orderData.items })

276

.toPromise();

277

278

// Create order

279

const order = await this.createOrder(orderData, payment.id);

280

281

// Send confirmation notification (fire-and-forget)

282

this.notificationClient.emit('order_created', {

283

orderId: order.id,

284

userId: user.id,

285

email: user.email,

286

items: orderData.items,

287

});

288

289

return order;

290

} catch (error) {

291

// Handle rollback if needed

292

await this.handleOrderFailure(orderData, error);

293

throw error;

294

}

295

}

296

297

private async createOrder(orderData: CreateOrderDto, paymentId: string): Promise<Order> {

298

// Local order creation logic

299

return this.orderRepository.create({

300

...orderData,

301

paymentId,

302

status: 'confirmed',

303

createdAt: new Date(),

304

});

305

}

306

307

private async handleOrderFailure(orderData: CreateOrderDto, error: Error): Promise<void> {

308

// Emit failure event for cleanup

309

this.notificationClient.emit('order_failed', {

310

userId: orderData.userId,

311

error: error.message,

312

timestamp: new Date().toISOString(),

313

});

314

}

315

}

316

```

317

318

**Client Health Monitoring:**

319

320

```typescript

321

import { Injectable, Logger } from '@nestjs/common';

322

import { ClientProxy } from '@nestjs/microservices';

323

import { Cron, CronExpression } from '@nestjs/schedule';

324

325

@Injectable()

326

export class ClientHealthService {

327

private readonly logger = new Logger(ClientHealthService.name);

328

329

constructor(

330

@Inject('USER_SERVICE') private userClient: ClientProxy,

331

@Inject('INVENTORY_SERVICE') private inventoryClient: ClientProxy,

332

) {}

333

334

@Cron(CronExpression.EVERY_30_SECONDS)

335

async checkClientHealth() {

336

const clients = [

337

{ name: 'USER_SERVICE', client: this.userClient },

338

{ name: 'INVENTORY_SERVICE', client: this.inventoryClient },

339

];

340

341

for (const { name, client } of clients) {

342

try {

343

await client.send({ cmd: 'health_check' }, {}).toPromise();

344

this.logger.log(`${name} is healthy`);

345

} catch (error) {

346

this.logger.error(`${name} health check failed:`, error.message);

347

348

// Attempt reconnection

349

try {

350

await client.close();

351

await client.connect();

352

this.logger.log(`${name} reconnected successfully`);

353

} catch (reconnectError) {

354

this.logger.error(`${name} reconnection failed:`, reconnectError.message);

355

}

356

}

357

}

358

}

359

}

360

```

361

362

**Configuration-Based Client Factory:**

363

364

```typescript

365

import { Injectable } from '@nestjs/common';

366

import { ConfigService } from '@nestjs/config';

367

import { ClientProxy, ClientProxyFactory, Transport } from '@nestjs/microservices';

368

369

interface ServiceConfig {

370

name: string;

371

transport: Transport;

372

options: any;

373

enabled: boolean;

374

}

375

376

@Injectable()

377

export class ClientFactory {

378

private clients: Map<string, ClientProxy> = new Map();

379

380

constructor(private configService: ConfigService) {}

381

382

async createClients(): Promise<void> {

383

const services = this.configService.get<ServiceConfig[]>('microservices');

384

385

for (const service of services) {

386

if (service.enabled) {

387

const client = ClientProxyFactory.create({

388

transport: service.transport,

389

options: service.options,

390

});

391

392

await client.connect();

393

this.clients.set(service.name, client);

394

}

395

}

396

}

397

398

getClient(name: string): ClientProxy {

399

const client = this.clients.get(name);

400

if (!client) {

401

throw new Error(`Client ${name} not found or not enabled`);

402

}

403

return client;

404

}

405

406

async closeAll(): Promise<void> {

407

const closePromises = Array.from(this.clients.values()).map(client => client.close());

408

await Promise.all(closePromises);

409

this.clients.clear();

410

}

411

}

412

413

// Usage with configuration

414

// config/microservices.config.ts

415

export default () => ({

416

microservices: [

417

{

418

name: 'USER_SERVICE',

419

transport: Transport.TCP,

420

options: {

421

host: process.env.USER_SERVICE_HOST || '127.0.0.1',

422

port: parseInt(process.env.USER_SERVICE_PORT) || 3001,

423

},

424

enabled: process.env.USER_SERVICE_ENABLED === 'true',

425

},

426

{

427

name: 'NOTIFICATION_SERVICE',

428

transport: Transport.REDIS,

429

options: {

430

host: process.env.REDIS_HOST || 'localhost',

431

port: parseInt(process.env.REDIS_PORT) || 6379,

432

},

433

enabled: process.env.NOTIFICATION_SERVICE_ENABLED === 'true',

434

},

435

],

436

});

437

```

438

439

### Testing Integration

440

441

Patterns for testing services with microservice clients.

442

443

```typescript

444

import { Test, TestingModule } from '@nestjs/testing';

445

import { ClientsModule, ClientProxy } from '@nestjs/microservices';

446

import { of } from 'rxjs';

447

448

describe('OrderService', () => {

449

let service: OrderService;

450

let userClient: ClientProxy;

451

let inventoryClient: ClientProxy;

452

453

beforeEach(async () => {

454

const mockUserClient = {

455

send: jest.fn(),

456

emit: jest.fn(),

457

connect: jest.fn().mockResolvedValue(undefined),

458

close: jest.fn().mockResolvedValue(undefined),

459

};

460

461

const mockInventoryClient = {

462

send: jest.fn(),

463

emit: jest.fn(),

464

connect: jest.fn().mockResolvedValue(undefined),

465

close: jest.fn().mockResolvedValue(undefined),

466

};

467

468

const module: TestingModule = await Test.createTestingModule({

469

providers: [

470

OrderService,

471

{

472

provide: 'USER_SERVICE',

473

useValue: mockUserClient,

474

},

475

{

476

provide: 'INVENTORY_SERVICE',

477

useValue: mockInventoryClient,

478

},

479

],

480

}).compile();

481

482

service = module.get<OrderService>(OrderService);

483

userClient = module.get<ClientProxy>('USER_SERVICE');

484

inventoryClient = module.get<ClientProxy>('INVENTORY_SERVICE');

485

});

486

487

it('should process order successfully', async () => {

488

// Setup mocks

489

(userClient.send as jest.Mock).mockReturnValue(

490

of({ id: 1, email: 'test@example.com' })

491

);

492

(inventoryClient.send as jest.Mock).mockReturnValue(

493

of({ available: true })

494

);

495

496

const orderData = {

497

userId: 1,

498

items: [{ id: 1, quantity: 2 }],

499

total: 100,

500

};

501

502

const result = await service.processOrder(orderData);

503

504

expect(result).toBeDefined();

505

expect(userClient.send).toHaveBeenCalledWith(

506

{ cmd: 'get_user' },

507

{ id: 1 }

508

);

509

expect(inventoryClient.send).toHaveBeenCalledWith(

510

{ cmd: 'check_availability' },

511

{ items: orderData.items }

512

);

513

});

514

});

515

```