nestjs-exception-handler 4.2.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,17 +3,31 @@
3
3
  [![npm version](https://badge.fury.io/js/nestjs-exception-handler.svg)](https://badge.fury.io/js/nestjs-exception-handler)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- A production-grade global exception handling system for NestJS applications that provides standardized error responses, supports Prisma errors, DTO validation, HTTP exceptions, and more.
6
+ A production-grade global exception handling system for NestJS applications that provides **consistent global error response format** for all types of errors.
7
+
8
+ ## Why This Package Exists
9
+
10
+ When building NestJS applications, error handling can become inconsistent across different scenarios:
11
+
12
+ - ValidationPipe errors from class-validator
13
+ - HttpException thrown manually
14
+ - Prisma database errors
15
+ - Unexpected internal server errors
16
+
17
+ This package ensures **every error response follows the same standardized format**, making your API consistent and easier to consume by clients.
7
18
 
8
19
  ## Features
9
20
 
10
- - **Standardized Error Responses**: Consistent error format across your application
11
- - **Prisma Integration**: Built-in support for Prisma error codes (P2002, P2003, etc.)
12
- - **DTO Validation**: Automatic extraction of validation errors from class-validator
13
- - **HTTP Exception Support**: Handles all NestJS HTTP exceptions
21
+ - **Standardized Error Responses**: Consistent error format across your entire application
22
+ - **ValidationPipe Support**: Automatic extraction and normalization of class-validator errors
23
+ - **Prisma Integration**: Built-in support for Prisma error codes (P2002, P2003, P2005, P2006, P2025)
24
+ - **HttpException Handling**: Handles all NestJS HTTP exceptions with proper formatting
25
+ - **404 Route Handling**: Converts unmatched routes to standardized format
26
+ - **Unknown Error Handling**: Safe fallback for unexpected errors without leaking stack traces
14
27
  - **Extensible**: Plugin system for custom formatters
15
- - **Type Safe**: Full TypeScript support
16
- - **Production Ready**: Includes logging and stack trace control
28
+ - **Type Safe**: Full TypeScript support with zero `any` types
29
+ - **Production Ready**: Configurable logging and stack trace control
30
+ - **NestJS v9 & v10 Compatible**: Works with both major versions
17
31
 
18
32
  ## Installation
19
33
 
@@ -21,204 +35,387 @@ A production-grade global exception handling system for NestJS applications that
21
35
  npm install nestjs-exception-handler
22
36
  ```
23
37
 
24
- ## Quick Start
38
+ Or with yarn:
25
39
 
26
- ### 1. Import the module
40
+ ```bash
41
+ yarn add nestjs-exception-handler
42
+ ```
27
43
 
28
- ```typescript
29
- import { Module } from '@nestjs/common';
30
- import { ExceptionHandlerModule } from 'nestjs-exception-handler';
44
+ ## Quick Setup
31
45
 
32
- @Module({
33
- imports: [ExceptionHandlerModule.forRoot()],
34
- })
35
- export class AppModule {}
36
- ```
46
+ ### 1. Apply the Global Filter
37
47
 
38
- ### 2. Apply the global filter
48
+ The simplest way to use the package is to apply the global exception filter:
39
49
 
40
50
  ```typescript
41
51
  import { NestFactory } from '@nestjs/core';
42
52
  import { AppModule } from './app.module';
43
53
  import { GlobalExceptionFilter } from 'nestjs-exception-handler';
54
+ import { ValidationPipe } from '@nestjs/common';
44
55
 
45
56
  async function bootstrap() {
46
57
  const app = await NestFactory.create(AppModule);
47
58
 
48
- const filter = app.get(GlobalExceptionFilter);
49
- app.useGlobalFilters(filter);
59
+ app.useGlobalPipes(
60
+ new ValidationPipe({
61
+ whitelist: true,
62
+ transform: true,
63
+ }),
64
+ );
65
+
66
+ app.useGlobalFilters(new GlobalExceptionFilter());
50
67
 
51
68
  await app.listen(3000);
52
69
  }
53
70
  bootstrap();
54
71
  ```
55
72
 
56
- ## Configuration
73
+ ### 2. Using with Module Registration (Optional)
74
+
75
+ For more control, you can use the module registration:
57
76
 
58
77
  ```typescript
59
- ExceptionHandlerModule.forRoot({
60
- enableLogging: true, // Enable structured logging
61
- hideStackTrace: true, // Hide stack traces in production
62
- });
78
+ import { Module } from '@nestjs/common';
79
+ import { ExceptionHandlerModule } from 'nestjs-exception-handler';
80
+
81
+ @Module({
82
+ imports: [
83
+ ExceptionHandlerModule.forRoot({
84
+ enableLogging: true,
85
+ hideStackTrace: true,
86
+ }),
87
+ ],
88
+ })
89
+ export class AppModule {}
63
90
  ```
64
91
 
65
- ## Standard Error Response Format
92
+ ## Error Response Format
66
93
 
67
- All responses follow this structure:
94
+ Every error response follows this exact structure:
95
+
96
+ ```typescript
97
+ interface ErrorResponse {
98
+ success: false;
99
+ message: string;
100
+ errorMessages: {
101
+ path: string;
102
+ message: string[];
103
+ }[];
104
+ }
105
+ ```
106
+
107
+ ### Example Response
68
108
 
69
109
  ```json
70
110
  {
71
111
  "success": false,
72
- "message": "Database error",
112
+ "message": "Bad Request Exception",
73
113
  "errorMessages": [
74
114
  {
75
- "path": "email",
76
- "message": "User with this email already exists"
115
+ "path": "http_error",
116
+ "message": ["email must be an email", "password must be longer than 8 characters"]
77
117
  }
78
118
  ]
79
119
  }
80
120
  ```
81
121
 
82
- ## Supported Error Types
122
+ ## Validation Example
83
123
 
84
- ### Prisma Errors
124
+ When using ValidationPipe with class-validator, errors are automatically normalized:
85
125
 
86
- Handles all Prisma error types with automatic path extraction:
126
+ ```typescript
127
+ // DTO
128
+ import { IsEmail, IsString, MinLength } from 'class-validator';
87
129
 
88
- - `P2002` - Unique constraint violation
89
- - `P2003` - Foreign key constraint violation
90
- - `P2005` - Invalid value
91
- - `P2006` - Invalid format
92
- - `P2025` - Record not found
130
+ export class CreateUserDto {
131
+ @IsEmail()
132
+ email: string;
133
+
134
+ @IsString()
135
+ @MinLength(8)
136
+ password: string;
137
+ }
138
+ ```
93
139
 
94
- **Example:**
140
+ **Response:**
95
141
 
96
142
  ```json
97
143
  {
98
- "path": "email",
99
- "message": "A record with this email already exists."
144
+ "success": false,
145
+ "message": "Bad Request Exception",
146
+ "errorMessages": [
147
+ {
148
+ "path": "http_error",
149
+ "message": ["email must be an email", "password must be longer than or equal to 8 characters"]
150
+ }
151
+ ]
100
152
  }
101
153
  ```
102
154
 
103
- ### DTO Validation Errors
155
+ ## HttpException Example
104
156
 
105
- Automatically extracts validation errors from class-validator:
157
+ Throwing HTTP exceptions manually:
106
158
 
107
159
  ```typescript
108
- // Example validation error
160
+ throw new BadRequestException('User already exists');
161
+ ```
162
+
163
+ **Response:**
164
+
165
+ ```json
109
166
  {
110
- "path": "email",
111
- "message": "email must be a valid email"
167
+ "success": false,
168
+ "message": "Bad Request Exception",
169
+ "errorMessages": [
170
+ {
171
+ "path": "http_error",
172
+ "message": ["User already exists"]
173
+ }
174
+ ]
112
175
  }
113
176
  ```
114
177
 
115
- ### HTTP Exceptions
178
+ Other supported HTTP exceptions:
116
179
 
117
- Handles all NestJS HTTP exceptions:
180
+ - `NotFoundException` - Returns "Not Found Exception"
181
+ - `UnauthorizedException` - Returns "Unauthorized Exception"
182
+ - `ForbiddenException` - Returns "Forbidden Exception"
183
+ - `ConflictException` - Returns "Conflict Exception"
184
+ - And all other NestJS HTTP exceptions
118
185
 
119
- ```typescript
120
- throw new BadRequestException('Invalid input');
121
- ```
186
+ ## Prisma Error Example
122
187
 
123
- ### Unknown Errors
188
+ The package automatically handles common Prisma errors:
124
189
 
125
- Fallback handler returns generic error message.
190
+ ### P2002 - Unique Constraint Violation
126
191
 
127
- ## Creating Custom Formatters
192
+ ```typescript
193
+ // When trying to create a user with duplicate email
194
+ ```
128
195
 
129
- Extend the `ExceptionFormatter` interface to create custom formatters:
196
+ **Response:**
130
197
 
131
- ```typescript
132
- import { ExceptionFormatter, ErrorMessage } from 'nestjs-exception-handler';
198
+ ```json
199
+ {
200
+ "success": false,
201
+ "message": "Database error",
202
+ "errorMessages": [
203
+ {
204
+ "path": "email",
205
+ "message": ["A record with this email already exists."]
206
+ }
207
+ ]
208
+ }
209
+ ```
133
210
 
134
- export class CustomExceptionFormatter implements ExceptionFormatter {
135
- supports(exception: unknown): boolean {
136
- return exception instanceof CustomError;
137
- }
211
+ ### P2003 - Foreign Key Constraint
138
212
 
139
- format(exception: unknown): ErrorMessage[] {
140
- const error = exception as CustomError;
141
- return [{ path: error.field, message: error.message }];
142
- }
213
+ **Response:**
143
214
 
144
- message(exception: unknown): string {
145
- return 'Custom error occurred';
146
- }
215
+ ```json
216
+ {
217
+ "success": false,
218
+ "message": "Database error",
219
+ "errorMessages": [
220
+ {
221
+ "path": "userId",
222
+ "message": ["The referenced userId does not exist."]
223
+ }
224
+ ]
147
225
  }
148
226
  ```
149
227
 
150
- ## API Reference
228
+ ### P2025 - Record Not Found
151
229
 
152
- ### ExceptionHandlerModule
230
+ **Response:**
153
231
 
154
- - `forRoot(config?)` - Register globally with optional configuration
155
- - `forFeature(config?)` - Register for specific modules
232
+ ```json
233
+ {
234
+ "success": false,
235
+ "message": "Database error",
236
+ "errorMessages": [
237
+ {
238
+ "path": "record",
239
+ "message": ["The requested record does not exist."]
240
+ }
241
+ ]
242
+ }
243
+ ```
156
244
 
157
- ### ExceptionHandlerService
245
+ ## Unknown Error Example
158
246
 
159
- - `registerFormatter(formatter)` - Add custom formatter
160
- - `formatException(exception)` - Format exception to standardized response
161
- - `formatErrors(exception)` - Get error messages array
162
- - `getErrorMessage(exception)` - Get error message string
163
- - `getAllFormatters()` - Get registered formatters
247
+ For unexpected errors:
164
248
 
165
- ### GlobalExceptionFilter
249
+ ```json
250
+ {
251
+ "success": false,
252
+ "message": "Internal Server Error",
253
+ "errorMessages": [
254
+ {
255
+ "path": "server",
256
+ "message": ["Something went wrong"]
257
+ }
258
+ ]
259
+ }
260
+ ```
166
261
 
167
- - Catches all unhandled exceptions
168
- - Logs structured errors
169
- - Returns standardized responses
262
+ Stack traces are never leaked in production (configurable).
170
263
 
171
- ## Error Response Examples
264
+ ## 404 Route Not Found Example
172
265
 
173
- ### Prisma Unique Constraint
266
+ When a client requests a route that doesn't exist, the package automatically converts it to the standardized format:
174
267
 
175
268
  ```json
176
269
  {
177
270
  "success": false,
178
- "message": "A record with this email already exists.",
271
+ "message": "Route Not Found",
179
272
  "errorMessages": [
180
273
  {
181
- "path": "email",
182
- "message": "A record with this email already exists."
274
+ "path": "route",
275
+ "message": ["Cannot GET /users/test"]
183
276
  }
184
277
  ]
185
278
  }
186
279
  ```
187
280
 
188
- ### Validation Error
281
+ The response includes:
282
+
283
+ - The HTTP method (GET, POST, PATCH, DELETE, etc.)
284
+ - The requested URL path
285
+
286
+ ### Examples for Different Methods
287
+
288
+ **POST to non-existent route:**
189
289
 
190
290
  ```json
191
291
  {
192
292
  "success": false,
193
- "message": "Validation failed",
293
+ "message": "Route Not Found",
194
294
  "errorMessages": [
195
295
  {
196
- "path": "email",
197
- "message": "email must be a valid email"
198
- },
199
- {
200
- "path": "password",
201
- "message": "password must be longer than 8 characters"
296
+ "path": "route",
297
+ "message": ["Cannot POST /api/user/test"]
202
298
  }
203
299
  ]
204
300
  }
205
301
  ```
206
302
 
207
- ### HTTP Exception
303
+ **DELETE on non-existent route:**
208
304
 
209
305
  ```json
210
306
  {
211
307
  "success": false,
212
- "message": "Resource not found",
308
+ "message": "Route Not Found",
213
309
  "errorMessages": [
214
310
  {
215
- "path": "unknown",
216
- "message": "Resource not found"
311
+ "path": "route",
312
+ "message": ["Cannot DELETE /api/items/123"]
217
313
  }
218
314
  ]
219
315
  }
220
316
  ```
221
317
 
318
+ ## Best Practices
319
+
320
+ ### 1. Always Use Global ValidationPipe
321
+
322
+ ```typescript
323
+ app.useGlobalPipes(
324
+ new ValidationPipe({
325
+ whitelist: true,
326
+ transform: true,
327
+ forbidNonWhitelisted: true,
328
+ }),
329
+ );
330
+ ```
331
+
332
+ ### 2. Configure Based on Environment
333
+
334
+ ```typescript
335
+ const config =
336
+ process.env.NODE_ENV === 'production'
337
+ ? { enableLogging: true, hideStackTrace: true }
338
+ : { enableLogging: true, hideStackTrace: false };
339
+
340
+ app.useGlobalFilters(new GlobalExceptionFilter(config));
341
+ ```
342
+
343
+ ### 3. Create Custom Formatters
344
+
345
+ Extend the package for custom error handling:
346
+
347
+ ```typescript
348
+ import { ExceptionFormatter, ErrorMessage } from 'nestjs-exception-handler';
349
+
350
+ export class CustomExceptionFormatter implements ExceptionFormatter {
351
+ supports(exception: unknown): boolean {
352
+ return exception instanceof CustomError;
353
+ }
354
+
355
+ format(exception: unknown): ErrorMessage[] {
356
+ const error = exception as CustomError;
357
+ return [
358
+ {
359
+ path: error.field,
360
+ message: [error.message],
361
+ },
362
+ ];
363
+ }
364
+
365
+ message(_exception: unknown): string {
366
+ return 'Custom error occurred';
367
+ }
368
+ }
369
+ ```
370
+
371
+ ## API Reference
372
+
373
+ ### GlobalExceptionFilter
374
+
375
+ ```typescript
376
+ // Constructor
377
+ new GlobalExceptionFilter(service: ExceptionHandlerService, config?: ExceptionHandlerConfig)
378
+
379
+ // Config options
380
+ interface ExceptionHandlerConfig {
381
+ enableLogging?: boolean; // Default: true
382
+ hideStackTrace?: boolean; // Default: false
383
+ }
384
+ ```
385
+
386
+ ### ExceptionHandlerModule
387
+
388
+ ```typescript
389
+ // Register globally
390
+ ExceptionHandlerModule.forRoot(config?)
391
+
392
+ // Register for specific feature
393
+ ExceptionHandlerModule.forFeature(config?)
394
+ ```
395
+
396
+ ### ExceptionHandlerService
397
+
398
+ ```typescript
399
+ // Register custom formatter
400
+ service.registerFormatter(formatter: ExceptionFormatter)
401
+
402
+ // Format exception
403
+ service.formatException(exception: unknown): { errors: ErrorMessage[]; message: string }
404
+
405
+ // Get all registered formatters
406
+ service.getAllFormatters(): ExceptionFormatter[]
407
+ ```
408
+
409
+ ## Response Examples Summary
410
+
411
+ | Error Type | Example |
412
+ | ------------- | ------------------------------------------------------- |
413
+ | Validation | `message: ["email must be an email"]` |
414
+ | HttpException | `message: ["User already exists"]` |
415
+ | Prisma P2002 | `message: ["A record with this email already exists."]` |
416
+ | 404 Not Found | `message: ["Cannot GET /unknown-route"]` |
417
+ | Unknown | `message: ["Something went wrong"]` |
418
+
222
419
  ## Contributing
223
420
 
224
421
  Contributions are welcome! Please feel free to submit a Pull Request.
package/dist/index.d.mts CHANGED
@@ -1,15 +1,14 @@
1
- import { PrismaClientKnownRequestError, PrismaClientValidationError, PrismaClientRustPanicError, PrismaClientInitializationError } from '@prisma/client/runtime/library';
2
1
  import { HttpException, ExceptionFilter, ArgumentsHost, DynamicModule } from '@nestjs/common';
2
+ import { PrismaClientKnownRequestError, PrismaClientValidationError, PrismaClientRustPanicError, PrismaClientInitializationError } from '@prisma/client/runtime/library';
3
3
 
4
- interface IErrorMessage {
4
+ interface ErrorMessage {
5
5
  path: string;
6
- message: string;
6
+ message: string[];
7
7
  }
8
- type ErrorMessage = IErrorMessage;
9
- interface StandardErrorResponse {
8
+ interface ErrorResponse {
10
9
  success: boolean;
11
10
  message: string;
12
- errorMessages: IErrorMessage[];
11
+ errorMessages: ErrorMessage[];
13
12
  }
14
13
 
15
14
  interface ExceptionFormatter {
@@ -23,12 +22,17 @@ interface ExceptionHandlerConfig {
23
22
  hideStackTrace?: boolean;
24
23
  }
25
24
 
25
+ declare class ValidationErrorFormatter {
26
+ formatValidationErrors(exception: HttpException): ErrorMessage[];
27
+ private formatNestedValidationErrors;
28
+ }
29
+
26
30
  type PrismaError = PrismaClientKnownRequestError | PrismaClientValidationError | PrismaClientRustPanicError | PrismaClientInitializationError | unknown;
27
31
  declare class PrismaExceptionFormatter implements ExceptionFormatter {
28
32
  supports(exception: unknown): boolean;
29
- format(exception: unknown): IErrorMessage[];
33
+ format(exception: unknown): ErrorMessage[];
30
34
  message(_exception: unknown): string;
31
- formatError(exception: PrismaError): IErrorMessage[];
35
+ formatError(exception: PrismaError): ErrorMessage[];
32
36
  private formatPrismaError;
33
37
  private formatQueryError;
34
38
  private formatInitializationError;
@@ -36,27 +40,14 @@ declare class PrismaExceptionFormatter implements ExceptionFormatter {
36
40
  }
37
41
 
38
42
  declare class DtoValidationFormatter {
39
- formatDtoValidationException(exception: HttpException): IErrorMessage[];
40
- }
41
-
42
- declare class OtherExceptionFormatter {
43
- formatOtherError(exception: unknown): IErrorMessage[];
44
- }
45
-
46
- declare class GlobalExceptionFilter implements ExceptionFilter {
47
- private readonly prismaExceptionFormatter;
48
- private readonly dtoValidationFormatter;
49
- private readonly otherValidationFormatter;
50
- constructor(prismaExceptionFormatter: PrismaExceptionFormatter, dtoValidationFormatter: DtoValidationFormatter, otherValidationFormatter: OtherExceptionFormatter);
51
- private readonly logger;
52
- private getErrorMessage;
53
- catch(exception: unknown, host: ArgumentsHost): void;
43
+ formatDtoValidationException(exception: HttpException): ErrorMessage[];
54
44
  }
55
45
 
56
46
  declare class ExceptionHandlerService {
57
47
  private formatters;
58
48
  private defaultFormatter;
59
49
  constructor();
50
+ private registerFormatters;
60
51
  registerFormatter(formatter: ExceptionFormatter): void;
61
52
  getFormatter(exception: unknown): ExceptionFormatter;
62
53
  formatException(exception: unknown): {
@@ -68,10 +59,26 @@ declare class ExceptionHandlerService {
68
59
  getAllFormatters(): ExceptionFormatter[];
69
60
  }
70
61
 
62
+ declare class GlobalExceptionFilter implements ExceptionFilter {
63
+ private exceptionHandlerService?;
64
+ private readonly logger;
65
+ private config;
66
+ constructor(exceptionHandlerService?: ExceptionHandlerService | undefined, config?: ExceptionHandlerConfig);
67
+ private getService;
68
+ catch(exception: unknown, host: ArgumentsHost): void;
69
+ private getStatusCode;
70
+ private logError;
71
+ }
72
+
71
73
  declare class ExceptionHandlerModule {
72
74
  static forRoot(config?: ExceptionHandlerConfig): DynamicModule;
73
75
  static forFeature(config?: ExceptionHandlerConfig): DynamicModule;
74
76
  }
75
77
  declare function initializeFormatters(service: ExceptionHandlerService): void;
76
78
 
77
- export { DtoValidationFormatter, ErrorMessage, ExceptionFormatter, ExceptionHandlerConfig, ExceptionHandlerModule, GlobalExceptionFilter, IErrorMessage, OtherExceptionFormatter, PrismaExceptionFormatter, StandardErrorResponse, initializeFormatters };
79
+ declare class HttpErrorFormatter {
80
+ formatHttpException(exception: HttpException): ErrorMessage[];
81
+ getMessage(exception: HttpException): string;
82
+ }
83
+
84
+ export { DtoValidationFormatter, ErrorMessage, ErrorResponse, ExceptionFormatter, ExceptionHandlerConfig, ExceptionHandlerModule, ExceptionHandlerService, GlobalExceptionFilter, HttpErrorFormatter, PrismaExceptionFormatter, ValidationErrorFormatter, initializeFormatters };