nestjs-exception-handler 4.1.0 → 4.3.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/dist/index.mjs CHANGED
@@ -10,6 +10,40 @@ var __decorateClass = (decorators, target, key, kind) => {
10
10
  return result;
11
11
  };
12
12
 
13
+ // src/formatters/validation-error.formatter.ts
14
+ var ValidationErrorFormatter = class {
15
+ formatValidationErrors(exception) {
16
+ const response = exception.getResponse();
17
+ if (typeof response === "object" && response !== null) {
18
+ const responseObj = response;
19
+ if (Array.isArray(responseObj.message)) {
20
+ const messages = responseObj.message;
21
+ if (messages.length > 0 && typeof messages[0] === "object" && "property" in messages[0]) {
22
+ return this.formatNestedValidationErrors(messages);
23
+ }
24
+ return [
25
+ {
26
+ path: "http_error",
27
+ message: messages
28
+ }
29
+ ];
30
+ }
31
+ }
32
+ return [
33
+ {
34
+ path: "http_error",
35
+ message: ["Validation failed"]
36
+ }
37
+ ];
38
+ }
39
+ formatNestedValidationErrors(errors) {
40
+ return errors.map((error) => ({
41
+ path: error.property,
42
+ message: error.constraints ? Object.values(error.constraints) : ["Validation error"]
43
+ }));
44
+ }
45
+ };
46
+
13
47
  // src/formatters/prisma-exception.formatter.ts
14
48
  import { Injectable } from "@nestjs/common";
15
49
  import {
@@ -19,6 +53,15 @@ import {
19
53
  PrismaClientInitializationError
20
54
  } from "@prisma/client/runtime/library";
21
55
  var PrismaExceptionFormatter = class {
56
+ supports(exception) {
57
+ return exception instanceof PrismaClientKnownRequestError || exception instanceof PrismaClientValidationError || exception instanceof PrismaClientRustPanicError || exception instanceof PrismaClientInitializationError;
58
+ }
59
+ format(exception) {
60
+ return this.formatError(exception);
61
+ }
62
+ message(_exception) {
63
+ return "Database error";
64
+ }
22
65
  formatError(exception) {
23
66
  if (exception instanceof PrismaClientKnownRequestError) {
24
67
  return this.formatPrismaError(exception);
@@ -41,7 +84,7 @@ var PrismaExceptionFormatter = class {
41
84
  return [
42
85
  {
43
86
  path: field,
44
- message: `A record with this ${field} already exists.`
87
+ message: [`A record with this ${field} already exists.`]
45
88
  }
46
89
  ];
47
90
  }
@@ -50,7 +93,7 @@ var PrismaExceptionFormatter = class {
50
93
  return [
51
94
  {
52
95
  path: fieldName || "field",
53
- message: `The referenced ${fieldName || "record"} does not exist.`
96
+ message: [`The referenced ${fieldName || "record"} does not exist.`]
54
97
  }
55
98
  ];
56
99
  }
@@ -59,7 +102,7 @@ var PrismaExceptionFormatter = class {
59
102
  return [
60
103
  {
61
104
  path: fieldName || "field",
62
- message: `The value for ${fieldName || "field"} is invalid.`
105
+ message: [`The value for ${fieldName || "field"} is invalid.`]
63
106
  }
64
107
  ];
65
108
  }
@@ -68,7 +111,7 @@ var PrismaExceptionFormatter = class {
68
111
  return [
69
112
  {
70
113
  path: fieldName || "field",
71
- message: `The ${fieldName || "field"} field is required.`
114
+ message: [`The ${fieldName || "field"} field is required.`]
72
115
  }
73
116
  ];
74
117
  }
@@ -76,7 +119,7 @@ var PrismaExceptionFormatter = class {
76
119
  return [
77
120
  {
78
121
  path: "record",
79
- message: "The requested record does not exist."
122
+ message: ["The requested record does not exist."]
80
123
  }
81
124
  ];
82
125
  }
@@ -84,7 +127,7 @@ var PrismaExceptionFormatter = class {
84
127
  return [
85
128
  {
86
129
  path: "database",
87
- message: "Database operation failed."
130
+ message: ["Database operation failed."]
88
131
  }
89
132
  ];
90
133
  }
@@ -97,7 +140,7 @@ var PrismaExceptionFormatter = class {
97
140
  return [
98
141
  {
99
142
  path: "database",
100
- message
143
+ message: [message]
101
144
  }
102
145
  ];
103
146
  }
@@ -105,7 +148,7 @@ var PrismaExceptionFormatter = class {
105
148
  return [
106
149
  {
107
150
  path: "database",
108
- message: `Database initialization error: ${exception.message}`
151
+ message: [`Database initialization error: ${exception.message}`]
109
152
  }
110
153
  ];
111
154
  }
@@ -113,7 +156,7 @@ var PrismaExceptionFormatter = class {
113
156
  return [
114
157
  {
115
158
  path: "unknown",
116
- message: exception instanceof Error ? exception.message : "An unexpected database error occurred."
159
+ message: exception instanceof Error ? [exception.message] : ["An unexpected database error occurred."]
117
160
  }
118
161
  ];
119
162
  }
@@ -134,14 +177,22 @@ var DtoValidationFormatter = class {
134
177
  const validationErrors = messages;
135
178
  return validationErrors.map((error) => ({
136
179
  path: error.property,
137
- message: error.constraints ? Object.values(error.constraints).join(", ") : "Validation error"
180
+ message: error.constraints ? Object.values(error.constraints) : ["Validation error"]
138
181
  }));
139
182
  }
183
+ if (typeof firstMessage === "string") {
184
+ return [
185
+ {
186
+ path: "http_error",
187
+ message: messages
188
+ }
189
+ ];
190
+ }
140
191
  }
141
192
  return [
142
193
  {
143
194
  path: "http_error",
144
- message: typeof responseBody === "object" && responseBody !== null && "message" in responseBody ? String(responseBody.message) : "An HTTP error occurred"
195
+ message: typeof responseBody === "object" && responseBody !== null && "message" in responseBody ? [responseBody.message] : ["An HTTP error occurred"]
145
196
  }
146
197
  ];
147
198
  }
@@ -150,112 +201,355 @@ DtoValidationFormatter = __decorateClass([
150
201
  Injectable2()
151
202
  ], DtoValidationFormatter);
152
203
 
153
- // src/formatters/other-exception.formatter.ts
154
- import { Injectable as Injectable3 } from "@nestjs/common";
155
- var OtherExceptionFormatter = class {
156
- formatOtherError(exception) {
157
- if (exception && typeof exception === "object" && "path" in exception && "message" in exception) {
158
- return [
159
- {
160
- path: String(exception.path),
161
- message: String(exception.message)
162
- }
163
- ];
164
- }
165
- const message = exception && typeof exception === "object" && "message" in exception ? String(exception.message) : "An unexpected error occurred";
166
- return [
167
- {
168
- path: "unknown",
169
- message
170
- }
171
- ];
172
- }
173
- };
174
- OtherExceptionFormatter = __decorateClass([
175
- Injectable3()
176
- ], OtherExceptionFormatter);
177
-
178
- // src/exception-filter/global-exception.filter.ts
204
+ // src/filter/global-exception.filter.ts
179
205
  import {
180
206
  Catch,
181
207
  HttpException,
182
208
  HttpStatus,
183
- Logger
209
+ Logger,
210
+ NotFoundException
184
211
  } from "@nestjs/common";
185
- import {
186
- PrismaClientKnownRequestError as PrismaClientKnownRequestError2,
187
- PrismaClientValidationError as PrismaClientValidationError2,
188
- PrismaClientRustPanicError as PrismaClientRustPanicError2,
189
- PrismaClientUnknownRequestError,
190
- PrismaClientInitializationError as PrismaClientInitializationError2
191
- } from "@prisma/client/runtime/library";
192
212
  var GlobalExceptionFilter = class {
193
- constructor(prismaExceptionFormatter, dtoValidationFormatter, otherValidationFormatter) {
194
- this.prismaExceptionFormatter = prismaExceptionFormatter;
195
- this.dtoValidationFormatter = dtoValidationFormatter;
196
- this.otherValidationFormatter = otherValidationFormatter;
213
+ constructor(exceptionHandlerService, config) {
214
+ this.exceptionHandlerService = exceptionHandlerService;
197
215
  this.logger = new Logger(GlobalExceptionFilter.name);
198
- }
199
- getErrorMessage(exception) {
200
- if (exception instanceof PrismaClientKnownRequestError2 || exception instanceof PrismaClientValidationError2 || exception instanceof PrismaClientRustPanicError2 || exception instanceof PrismaClientUnknownRequestError || exception instanceof PrismaClientInitializationError2) {
201
- return "Database error";
202
- }
203
- if (exception instanceof HttpException) {
204
- return exception.message || "HTTP error";
205
- }
206
- return "Internal server error";
216
+ this.config = config || { enableLogging: true, hideStackTrace: false };
207
217
  }
208
218
  catch(exception, host) {
209
219
  const ctx = host.switchToHttp();
210
220
  const response = ctx.getResponse();
211
221
  const request = ctx.getRequest();
212
- const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
213
- let errorMessages = [{ path: "unknown", message: "Internal server error" }];
214
- if (exception instanceof PrismaClientKnownRequestError2 || exception instanceof PrismaClientValidationError2 || exception instanceof PrismaClientRustPanicError2 || exception instanceof PrismaClientUnknownRequestError || exception instanceof PrismaClientInitializationError2) {
215
- errorMessages = this.prismaExceptionFormatter.formatError(exception);
216
- } else if (exception instanceof HttpException) {
217
- errorMessages = this.dtoValidationFormatter.formatDtoValidationException(
218
- exception
219
- );
220
- } else {
221
- errorMessages = this.otherValidationFormatter.formatOtherError(exception);
222
+ const { errors, message } = this.exceptionHandlerService.formatException(exception);
223
+ let status = this.getStatusCode(exception);
224
+ let finalErrors = errors;
225
+ let finalMessage = message;
226
+ if (exception instanceof NotFoundException) {
227
+ const exceptionResponse = exception.getResponse();
228
+ if (typeof exceptionResponse === "object" && exceptionResponse !== null && "message" in exceptionResponse) {
229
+ const msg = exceptionResponse.message;
230
+ if (typeof msg === "string" && msg.includes("Cannot")) {
231
+ status = HttpStatus.NOT_FOUND;
232
+ finalMessage = "Route Not Found";
233
+ finalErrors = [
234
+ {
235
+ path: "route",
236
+ message: [msg]
237
+ }
238
+ ];
239
+ }
240
+ }
222
241
  }
223
- this.logger.error(
224
- `${request.method} ${request.url}`,
225
- JSON.stringify(errorMessages),
226
- "ExceptionFilter"
227
- );
228
- response.status(status).json({
242
+ const errorResponse = {
229
243
  success: false,
230
- message: this.getErrorMessage(exception),
231
- errorMessages
232
- });
244
+ message: finalMessage,
245
+ errorMessages: finalErrors
246
+ };
247
+ if (this.config.enableLogging) {
248
+ this.logError(request, status, finalErrors, exception);
249
+ }
250
+ response.status(status).json(errorResponse);
251
+ }
252
+ getStatusCode(exception) {
253
+ if (exception instanceof HttpException) {
254
+ return exception.getStatus();
255
+ }
256
+ return HttpStatus.INTERNAL_SERVER_ERROR;
257
+ }
258
+ logError(request, status, errorMessages, exception) {
259
+ const method = request.method;
260
+ const url = request.url;
261
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
262
+ const logData = {
263
+ timestamp,
264
+ method,
265
+ url,
266
+ status,
267
+ errorMessages,
268
+ stack: this.config.hideStackTrace ? void 0 : exception?.stack
269
+ };
270
+ this.logger.error(`${method} ${url} - ${status}`, JSON.stringify(logData));
233
271
  }
234
272
  };
235
273
  GlobalExceptionFilter = __decorateClass([
236
274
  Catch()
237
275
  ], GlobalExceptionFilter);
238
276
 
239
- // src/exception-handler.module.ts
277
+ // src/module/exception-handler.module.ts
240
278
  import { Module } from "@nestjs/common";
279
+
280
+ // src/services/exception-handler.service.ts
281
+ import { Injectable as Injectable3 } from "@nestjs/common";
282
+
283
+ // src/formatters/http-exception.formatter.ts
284
+ import { HttpException as HttpException2 } from "@nestjs/common";
285
+ var HttpExceptionFormatter = class {
286
+ supports(exception) {
287
+ return exception instanceof HttpException2;
288
+ }
289
+ format(exception) {
290
+ const httpException = exception;
291
+ const response = httpException.getResponse();
292
+ if (typeof response === "string") {
293
+ return [{ path: "http_error", message: [response] }];
294
+ }
295
+ if (typeof response === "object" && response !== null) {
296
+ const responseObj = response;
297
+ if (responseObj.message && Array.isArray(responseObj.message)) {
298
+ return [
299
+ {
300
+ path: "http_error",
301
+ message: responseObj.message
302
+ }
303
+ ];
304
+ }
305
+ if (responseObj.message && typeof responseObj.message === "string") {
306
+ return [{ path: "http_error", message: [responseObj.message] }];
307
+ }
308
+ if (responseObj.error && typeof responseObj.error === "string") {
309
+ return [{ path: "http_error", message: [responseObj.error] }];
310
+ }
311
+ }
312
+ return [{ path: "http_error", message: ["An error occurred"] }];
313
+ }
314
+ message(exception) {
315
+ const httpException = exception;
316
+ const response = httpException.getResponse();
317
+ if (typeof response === "string") {
318
+ return response;
319
+ }
320
+ if (typeof response === "object" && response !== null) {
321
+ const responseObj = response;
322
+ if (responseObj.message && typeof responseObj.message === "string") {
323
+ return responseObj.message;
324
+ }
325
+ if (responseObj.error && typeof responseObj.error === "string") {
326
+ return responseObj.error;
327
+ }
328
+ }
329
+ return "An error occurred";
330
+ }
331
+ };
332
+
333
+ // src/formatters/dto-exception.formatter.ts
334
+ var DtoExceptionFormatter = class {
335
+ supports(exception) {
336
+ if (!exception || typeof exception !== "object") {
337
+ return false;
338
+ }
339
+ const error = exception;
340
+ return Array.isArray(error.validationErrors) || Array.isArray(error.children) && error.constraints !== void 0;
341
+ }
342
+ format(exception) {
343
+ const error = exception;
344
+ const validationErrors = error.validationErrors;
345
+ const children = error.children;
346
+ if (validationErrors) {
347
+ return this.formatValidationErrors(validationErrors);
348
+ }
349
+ if (children) {
350
+ return this.formatChildrenErrors(children);
351
+ }
352
+ return [{ path: "unknown", message: ["Validation failed"] }];
353
+ }
354
+ message(exception) {
355
+ const error = exception;
356
+ const validationErrors = error.validationErrors;
357
+ const children = error.children;
358
+ if (validationErrors && validationErrors.length > 0) {
359
+ const firstError = validationErrors[0];
360
+ const constraints = firstError.constraints;
361
+ if (constraints) {
362
+ return Object.values(constraints)[0];
363
+ }
364
+ }
365
+ if (children && children.length > 0) {
366
+ return "Validation failed for nested fields";
367
+ }
368
+ return "Validation failed";
369
+ }
370
+ formatValidationErrors(errors) {
371
+ return errors.flatMap((error) => {
372
+ const constraints = error.constraints;
373
+ if (constraints) {
374
+ return [
375
+ {
376
+ path: error.property,
377
+ message: Object.values(constraints)
378
+ }
379
+ ];
380
+ }
381
+ return [];
382
+ });
383
+ }
384
+ formatChildrenErrors(children) {
385
+ return children.flatMap((child) => {
386
+ const constraints = child.constraints;
387
+ if (constraints) {
388
+ return [
389
+ {
390
+ path: child.property,
391
+ message: Object.values(constraints)
392
+ }
393
+ ];
394
+ }
395
+ return [];
396
+ });
397
+ }
398
+ };
399
+
400
+ // src/constants/default-messages.ts
401
+ var DEFAULT_PATH = "unknown";
402
+
403
+ // src/formatters/unknown-exception.formatter.ts
404
+ var UnknownExceptionFormatter = class {
405
+ supports(_exception) {
406
+ return true;
407
+ }
408
+ format(_exception) {
409
+ return [
410
+ {
411
+ path: DEFAULT_PATH,
412
+ message: ["Something went wrong"]
413
+ }
414
+ ];
415
+ }
416
+ message(_exception) {
417
+ return "Internal Server Error";
418
+ }
419
+ };
420
+
421
+ // src/services/exception-handler.service.ts
422
+ var ExceptionHandlerService = class {
423
+ constructor() {
424
+ this.formatters = [];
425
+ this.defaultFormatter = new UnknownExceptionFormatter();
426
+ this.registerFormatters();
427
+ }
428
+ registerFormatters() {
429
+ this.formatters = [
430
+ new PrismaExceptionFormatter(),
431
+ new HttpExceptionFormatter(),
432
+ new DtoExceptionFormatter()
433
+ ];
434
+ }
435
+ registerFormatter(formatter) {
436
+ this.formatters.push(formatter);
437
+ }
438
+ getFormatter(exception) {
439
+ for (const formatter of this.formatters) {
440
+ if (formatter.supports(exception)) {
441
+ return formatter;
442
+ }
443
+ }
444
+ return this.defaultFormatter;
445
+ }
446
+ formatException(exception) {
447
+ const formatter = this.getFormatter(exception);
448
+ const errors = formatter.format(exception);
449
+ const message = formatter.message(exception);
450
+ return { errors, message };
451
+ }
452
+ formatErrors(exception) {
453
+ const formatter = this.getFormatter(exception);
454
+ return formatter.format(exception);
455
+ }
456
+ getErrorMessage(exception) {
457
+ const formatter = this.getFormatter(exception);
458
+ return formatter.message(exception);
459
+ }
460
+ getAllFormatters() {
461
+ return [...this.formatters];
462
+ }
463
+ };
464
+ ExceptionHandlerService = __decorateClass([
465
+ Injectable3()
466
+ ], ExceptionHandlerService);
467
+
468
+ // src/module/exception-handler.module.ts
241
469
  var ExceptionHandlerModule = class {
470
+ static forRoot(config) {
471
+ const providers = [
472
+ ExceptionHandlerService,
473
+ {
474
+ provide: GlobalExceptionFilter,
475
+ useFactory: (service) => {
476
+ return new GlobalExceptionFilter(service, config);
477
+ },
478
+ inject: [ExceptionHandlerService]
479
+ }
480
+ ];
481
+ return {
482
+ module: ExceptionHandlerModule,
483
+ providers,
484
+ exports: [ExceptionHandlerService, GlobalExceptionFilter],
485
+ global: true
486
+ };
487
+ }
488
+ static forFeature(config) {
489
+ return this.forRoot(config);
490
+ }
242
491
  };
243
492
  ExceptionHandlerModule = __decorateClass([
244
- Module({
245
- providers: [
246
- GlobalExceptionFilter,
247
- PrismaExceptionFormatter,
248
- DtoValidationFormatter,
249
- OtherExceptionFormatter
250
- ],
251
- exports: [GlobalExceptionFilter]
252
- })
493
+ Module({})
253
494
  ], ExceptionHandlerModule);
495
+ function initializeFormatters(service) {
496
+ service.registerFormatter(new PrismaExceptionFormatter());
497
+ service.registerFormatter(new DtoExceptionFormatter());
498
+ service.registerFormatter(new HttpExceptionFormatter());
499
+ service.registerFormatter(new UnknownExceptionFormatter());
500
+ }
501
+
502
+ // src/utils/http-error.formatter.ts
503
+ var HttpErrorFormatter = class {
504
+ formatHttpException(exception) {
505
+ const response = exception.getResponse();
506
+ if (typeof response === "string") {
507
+ return [{ path: "http_error", message: [response] }];
508
+ }
509
+ if (typeof response === "object" && response !== null) {
510
+ const responseObj = response;
511
+ if (Array.isArray(responseObj.message)) {
512
+ return [
513
+ {
514
+ path: "http_error",
515
+ message: responseObj.message
516
+ }
517
+ ];
518
+ }
519
+ if (typeof responseObj.message === "string") {
520
+ return [{ path: "http_error", message: [responseObj.message] }];
521
+ }
522
+ if (responseObj.error && typeof responseObj.error === "string") {
523
+ return [{ path: "http_error", message: [responseObj.error] }];
524
+ }
525
+ }
526
+ return [{ path: "http_error", message: ["An error occurred"] }];
527
+ }
528
+ getMessage(exception) {
529
+ const response = exception.getResponse();
530
+ if (typeof response === "string") {
531
+ return response;
532
+ }
533
+ if (typeof response === "object" && response !== null) {
534
+ const responseObj = response;
535
+ if (typeof responseObj.message === "string") {
536
+ return responseObj.message;
537
+ }
538
+ if (responseObj.error && typeof responseObj.error === "string") {
539
+ return responseObj.error;
540
+ }
541
+ }
542
+ return "An error occurred";
543
+ }
544
+ };
254
545
  export {
255
546
  DtoValidationFormatter,
256
547
  ExceptionHandlerModule,
548
+ ExceptionHandlerService,
257
549
  GlobalExceptionFilter,
258
- OtherExceptionFormatter,
259
- PrismaExceptionFormatter
550
+ HttpErrorFormatter,
551
+ PrismaExceptionFormatter,
552
+ ValidationErrorFormatter,
553
+ initializeFormatters
260
554
  };
261
555
  //# sourceMappingURL=index.mjs.map