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

exceptions.mddocs/

0

# Exception Handling

1

2

Microservice-specific exception classes and error handling patterns for distributed system error management, providing structured error responses and transport-specific error handling capabilities.

3

4

## Capabilities

5

6

### RPC Exception

7

8

Base exception class for RPC operations providing structured error handling across all transport types.

9

10

```typescript { .api }

11

/**

12

* Base exception class for RPC operations

13

*/

14

class RpcException extends Error {

15

/**

16

* Creates a new RPC exception

17

* @param error - Error message string or structured error object

18

*/

19

constructor(error: string | object);

20

21

/**

22

* Returns the error data

23

* @returns Original error message or object

24

*/

25

getError(): string | object;

26

}

27

```

28

29

**Usage Examples:**

30

31

```typescript

32

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

33

import { MessagePattern, RpcException } from '@nestjs/microservices';

34

35

@Controller()

36

export class UserController {

37

@MessagePattern({ cmd: 'get_user' })

38

getUser(data: { id: number }): any {

39

if (!data.id) {

40

throw new RpcException('User ID is required');

41

}

42

43

const user = this.userService.findById(data.id);

44

if (!user) {

45

throw new RpcException({

46

code: 'USER_NOT_FOUND',

47

message: `User with ID ${data.id} not found`,

48

statusCode: 404

49

});

50

}

51

52

return user;

53

}

54

55

@MessagePattern({ cmd: 'create_user' })

56

createUser(data: any): any {

57

try {

58

this.validateUserData(data);

59

return this.userService.create(data);

60

} catch (validationError) {

61

throw new RpcException({

62

code: 'VALIDATION_ERROR',

63

message: 'Invalid user data provided',

64

details: validationError.details,

65

statusCode: 400

66

});

67

}

68

}

69

70

@MessagePattern({ cmd: 'update_user' })

71

async updateUser(data: { id: number; updates: any }): Promise<any> {

72

try {

73

const result = await this.userService.update(data.id, data.updates);

74

return result;

75

} catch (error) {

76

if (error.code === 'CONCURRENT_MODIFICATION') {

77

throw new RpcException({

78

code: 'CONFLICT',

79

message: 'User was modified by another process',

80

timestamp: new Date().toISOString(),

81

statusCode: 409

82

});

83

}

84

85

// Re-throw as RPC exception

86

throw new RpcException({

87

code: 'INTERNAL_ERROR',

88

message: 'Failed to update user',

89

originalError: error.message

90

});

91

}

92

}

93

}

94

```

95

96

### Kafka Retriable Exception

97

98

Specialized exception for Kafka operations that can be retried, enabling proper error handling in streaming scenarios.

99

100

```typescript { .api }

101

/**

102

* Kafka-specific exception for retriable errors

103

*/

104

class KafkaRetriableException extends RpcException {

105

/**

106

* Creates a new Kafka retriable exception

107

* @param error - Error message string or structured error object

108

*/

109

constructor(error: string | object);

110

}

111

```

112

113

**Usage Examples:**

114

115

```typescript

116

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

117

import { EventPattern, MessagePattern, Payload, Ctx } from '@nestjs/microservices';

118

import { KafkaRetriableException, KafkaContext } from '@nestjs/microservices';

119

120

@Controller()

121

export class KafkaErrorController {

122

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

123

124

@EventPattern('user.events.process')

125

async processUserEvent(

126

@Payload() event: any,

127

@Ctx() context: KafkaContext

128

): Promise<void> {

129

try {

130

await this.processEvent(event);

131

} catch (error) {

132

if (this.isRetriableError(error)) {

133

// Throw retriable exception - Kafka will retry

134

throw new KafkaRetriableException({

135

code: 'TEMPORARY_FAILURE',

136

message: 'Temporary processing failure, will retry',

137

originalError: error.message,

138

retryCount: this.getRetryCount(context),

139

topic: context.getTopic(),

140

partition: context.getPartition()

141

});

142

} else {

143

// Non-retriable error - log and continue

144

this.logger.error('Non-retriable error processing event:', error);

145

this.deadLetterService.send(event, error);

146

}

147

}

148

}

149

150

@MessagePattern('user.commands.process')

151

async processUserCommand(

152

@Payload() command: any,

153

@Ctx() context: KafkaContext

154

): Promise<any> {

155

const maxRetries = 3;

156

const retryCount = this.getRetryCount(context);

157

158

try {

159

return await this.commandService.process(command);

160

} catch (error) {

161

if (retryCount < maxRetries && this.shouldRetry(error)) {

162

throw new KafkaRetriableException({

163

code: 'PROCESSING_FAILED',

164

message: `Command processing failed, retry ${retryCount + 1}/${maxRetries}`,

165

commandId: command.id,

166

retryCount: retryCount,

167

maxRetries: maxRetries,

168

error: error.message

169

});

170

} else {

171

// Max retries reached or non-retriable error

172

throw new RpcException({

173

code: 'COMMAND_FAILED',

174

message: 'Command processing failed permanently',

175

commandId: command.id,

176

retryCount: retryCount,

177

finalError: error.message

178

});

179

}

180

}

181

}

182

183

private isRetriableError(error: any): boolean {

184

// Define logic for determining if error is retriable

185

return error.code === 'ECONNRESET' ||

186

error.code === 'ETIMEDOUT' ||

187

error.statusCode === 503 ||

188

error.statusCode === 429;

189

}

190

191

private shouldRetry(error: any): boolean {

192

return error.temporary === true ||

193

error.statusCode >= 500 ||

194

error.code === 'NETWORK_ERROR';

195

}

196

197

private getRetryCount(context: KafkaContext): number {

198

const message = context.getMessage();

199

const retryHeader = message.headers?.['retry-count'];

200

return retryHeader ? parseInt(retryHeader.toString()) : 0;

201

}

202

}

203

```

204

205

### Base RPC Exception Filter

206

207

Base exception filter for handling RPC exceptions across different transport types.

208

209

```typescript { .api }

210

/**

211

* Base exception filter for RPC operations

212

*/

213

class BaseRpcExceptionFilter {

214

/**

215

* Catches and processes RPC exceptions

216

* @param exception - The thrown exception

217

* @param host - Execution context host

218

*/

219

catch(exception: RpcException, host: any): any;

220

}

221

```

222

223

**Usage Examples:**

224

225

```typescript

226

import { Catch, RpcExceptionFilter, ArgumentsHost } from '@nestjs/common';

227

import { Observable, throwError } from 'rxjs';

228

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

229

230

@Catch(RpcException)

231

export class CustomRpcExceptionFilter implements RpcExceptionFilter<RpcException> {

232

catch(exception: RpcException, host: ArgumentsHost): Observable<any> {

233

const ctx = host.switchToRpc();

234

const data = ctx.getData();

235

const error = exception.getError();

236

237

// Log the error

238

console.error('RPC Exception:', {

239

error,

240

requestData: data,

241

timestamp: new Date().toISOString()

242

});

243

244

// Format error response

245

const errorResponse = {

246

success: false,

247

error: typeof error === 'string' ? { message: error } : error,

248

timestamp: new Date().toISOString(),

249

requestId: this.generateRequestId()

250

};

251

252

return throwError(() => errorResponse);

253

}

254

255

private generateRequestId(): string {

256

return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

257

}

258

}

259

260

// Usage in main.ts or module

261

import { NestFactory } from '@nestjs/core';

262

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

263

264

async function bootstrap() {

265

const app = await NestFactory.createMicroservice<MicroserviceOptions>(

266

AppModule,

267

{

268

transport: Transport.TCP,

269

options: { port: 3001 },

270

},

271

);

272

273

app.useGlobalFilters(new CustomRpcExceptionFilter());

274

await app.listen();

275

}

276

```

277

278

### Transport-Specific Error Handling

279

280

Different error handling patterns for various transport types.

281

282

**TCP Error Handling:**

283

284

```typescript

285

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

286

import { MessagePattern, RpcException, Ctx, TcpContext } from '@nestjs/microservices';

287

288

@Controller()

289

export class TcpErrorController {

290

@MessagePattern({ cmd: 'risky_operation' })

291

performRiskyOperation(

292

data: any,

293

@Ctx() context: TcpContext

294

): any {

295

try {

296

return this.processData(data);

297

} catch (error) {

298

// TCP-specific error handling

299

const socket = context.getSocketRef();

300

301

throw new RpcException({

302

code: 'TCP_PROCESSING_ERROR',

303

message: error.message,

304

clientAddress: socket.socket.remoteAddress,

305

clientPort: socket.socket.remotePort,

306

timestamp: new Date().toISOString()

307

});

308

}

309

}

310

}

311

```

312

313

**Redis Error Handling:**

314

315

```typescript

316

@Controller()

317

export class RedisErrorController {

318

@MessagePattern('cache:operation')

319

performCacheOperation(

320

data: any,

321

@Ctx() context: RedisContext

322

): any {

323

try {

324

return this.cacheService.process(data);

325

} catch (error) {

326

throw new RpcException({

327

code: 'REDIS_ERROR',

328

message: 'Cache operation failed',

329

channel: context.getChannel(),

330

error: error.message,

331

retryable: error.code === 'ECONNRESET'

332

});

333

}

334

}

335

}

336

```

337

338

**gRPC Error Handling:**

339

340

```typescript

341

import { status } from '@grpc/grpc-js';

342

343

@Controller()

344

export class GrpcErrorController {

345

@GrpcMethod('UserService', 'GetUser')

346

getUser(data: { id: string }): any {

347

try {

348

const user = this.userService.findById(data.id);

349

if (!user) {

350

throw new RpcException({

351

code: status.NOT_FOUND,

352

message: `User with ID ${data.id} not found`,

353

details: 'USER_NOT_FOUND'

354

});

355

}

356

return user;

357

} catch (error) {

358

if (error.name === 'ValidationError') {

359

throw new RpcException({

360

code: status.INVALID_ARGUMENT,

361

message: 'Invalid request data',

362

details: error.details

363

});

364

}

365

366

throw new RpcException({

367

code: status.INTERNAL,

368

message: 'Internal server error'

369

});

370

}

371

}

372

}

373

```

374

375

### Error Response Patterns

376

377

Common patterns for structuring error responses across different scenarios.

378

379

```typescript

380

// Standard error response structure

381

interface ErrorResponse {

382

success: false;

383

error: {

384

code: string;

385

message: string;

386

details?: any;

387

timestamp: string;

388

requestId?: string;

389

};

390

}

391

392

// Validation error structure

393

interface ValidationErrorResponse extends ErrorResponse {

394

error: {

395

code: 'VALIDATION_ERROR';

396

message: string;

397

fields: Array<{

398

field: string;

399

message: string;

400

value?: any;

401

}>;

402

timestamp: string;

403

};

404

}

405

406

// Business logic error structure

407

interface BusinessErrorResponse extends ErrorResponse {

408

error: {

409

code: string;

410

message: string;

411

businessRules: string[];

412

timestamp: string;

413

context?: Record<string, any>;

414

};

415

}

416

417

// Usage examples

418

@Controller()

419

export class StructuredErrorController {

420

@MessagePattern({ cmd: 'validate_and_process' })

421

validateAndProcess(data: any): any {

422

// Validation errors

423

const validationErrors = this.validate(data);

424

if (validationErrors.length > 0) {

425

throw new RpcException({

426

code: 'VALIDATION_ERROR',

427

message: 'Request validation failed',

428

fields: validationErrors,

429

timestamp: new Date().toISOString()

430

});

431

}

432

433

// Business logic errors

434

if (!this.checkBusinessRules(data)) {

435

throw new RpcException({

436

code: 'BUSINESS_RULE_VIOLATION',

437

message: 'Business rules validation failed',

438

businessRules: ['INSUFFICIENT_BALANCE', 'ACCOUNT_SUSPENDED'],

439

timestamp: new Date().toISOString(),

440

context: { accountId: data.accountId, amount: data.amount }

441

});

442

}

443

444

return this.processData(data);

445

}

446

}

447

```