nestjs-exception-handler 1.0.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 +240 -0
- package/dist/index.d.mts +96 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.js +384 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +354 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# NestJS Exception Handler
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/nestjs-exception-handler)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
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.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
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
|
|
14
|
+
- **Extensible**: Plugin system for custom formatters
|
|
15
|
+
- **Type Safe**: Full TypeScript support
|
|
16
|
+
- **Production Ready**: Includes logging and stack trace control
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install nestjs-exception-handler
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### 1. Import the module
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { Module } from '@nestjs/common';
|
|
30
|
+
import { ExceptionHandlerModule } from 'nestjs-exception-handler';
|
|
31
|
+
|
|
32
|
+
@Module({
|
|
33
|
+
imports: [ExceptionHandlerModule.forRoot()],
|
|
34
|
+
})
|
|
35
|
+
export class AppModule {}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Apply the global filter
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { NestFactory } from '@nestjs/core';
|
|
42
|
+
import { AppModule } from './app.module';
|
|
43
|
+
import { GlobalExceptionFilter } from 'nestjs-exception-handler';
|
|
44
|
+
|
|
45
|
+
async function bootstrap() {
|
|
46
|
+
const app = await NestFactory.create(AppModule);
|
|
47
|
+
|
|
48
|
+
const filter = app.get(GlobalExceptionFilter);
|
|
49
|
+
app.useGlobalFilters(filter);
|
|
50
|
+
|
|
51
|
+
await app.listen(3000);
|
|
52
|
+
}
|
|
53
|
+
bootstrap();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Configuration
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
ExceptionHandlerModule.forRoot({
|
|
60
|
+
enableLogging: true, // Enable structured logging
|
|
61
|
+
hideStackTrace: true, // Hide stack traces in production
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Standard Error Response Format
|
|
66
|
+
|
|
67
|
+
All responses follow this structure:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"success": false,
|
|
72
|
+
"message": "Database error",
|
|
73
|
+
"errorMessages": [
|
|
74
|
+
{
|
|
75
|
+
"path": "email",
|
|
76
|
+
"message": "User with this email already exists"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Supported Error Types
|
|
83
|
+
|
|
84
|
+
### Prisma Errors
|
|
85
|
+
|
|
86
|
+
Handles all Prisma error types with automatic path extraction:
|
|
87
|
+
|
|
88
|
+
- `P2002` - Unique constraint violation
|
|
89
|
+
- `P2003` - Foreign key constraint violation
|
|
90
|
+
- `P2005` - Invalid value
|
|
91
|
+
- `P2006` - Invalid format
|
|
92
|
+
- `P2025` - Record not found
|
|
93
|
+
|
|
94
|
+
**Example:**
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"path": "email",
|
|
99
|
+
"message": "A record with this email already exists."
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### DTO Validation Errors
|
|
104
|
+
|
|
105
|
+
Automatically extracts validation errors from class-validator:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// Example validation error
|
|
109
|
+
{
|
|
110
|
+
"path": "email",
|
|
111
|
+
"message": "email must be a valid email"
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### HTTP Exceptions
|
|
116
|
+
|
|
117
|
+
Handles all NestJS HTTP exceptions:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
throw new BadRequestException('Invalid input');
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Unknown Errors
|
|
124
|
+
|
|
125
|
+
Fallback handler returns generic error message.
|
|
126
|
+
|
|
127
|
+
## Creating Custom Formatters
|
|
128
|
+
|
|
129
|
+
Extend the `ExceptionFormatter` interface to create custom formatters:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { ExceptionFormatter, ErrorMessage } from 'nestjs-exception-handler';
|
|
133
|
+
|
|
134
|
+
export class CustomExceptionFormatter implements ExceptionFormatter {
|
|
135
|
+
supports(exception: unknown): boolean {
|
|
136
|
+
return exception instanceof CustomError;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
format(exception: unknown): ErrorMessage[] {
|
|
140
|
+
const error = exception as CustomError;
|
|
141
|
+
return [{ path: error.field, message: error.message }];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
message(exception: unknown): string {
|
|
145
|
+
return 'Custom error occurred';
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## API Reference
|
|
151
|
+
|
|
152
|
+
### ExceptionHandlerModule
|
|
153
|
+
|
|
154
|
+
- `forRoot(config?)` - Register globally with optional configuration
|
|
155
|
+
- `forFeature(config?)` - Register for specific modules
|
|
156
|
+
|
|
157
|
+
### ExceptionHandlerService
|
|
158
|
+
|
|
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
|
|
164
|
+
|
|
165
|
+
### GlobalExceptionFilter
|
|
166
|
+
|
|
167
|
+
- Catches all unhandled exceptions
|
|
168
|
+
- Logs structured errors
|
|
169
|
+
- Returns standardized responses
|
|
170
|
+
|
|
171
|
+
## Error Response Examples
|
|
172
|
+
|
|
173
|
+
### Prisma Unique Constraint
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"success": false,
|
|
178
|
+
"message": "A record with this email already exists.",
|
|
179
|
+
"errorMessages": [
|
|
180
|
+
{
|
|
181
|
+
"path": "email",
|
|
182
|
+
"message": "A record with this email already exists."
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Validation Error
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"success": false,
|
|
193
|
+
"message": "Validation failed",
|
|
194
|
+
"errorMessages": [
|
|
195
|
+
{
|
|
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"
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### HTTP Exception
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"success": false,
|
|
212
|
+
"message": "Resource not found",
|
|
213
|
+
"errorMessages": [
|
|
214
|
+
{
|
|
215
|
+
"path": "unknown",
|
|
216
|
+
"message": "Resource not found"
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Contributing
|
|
223
|
+
|
|
224
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
225
|
+
|
|
226
|
+
1. Fork the repository
|
|
227
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
228
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
229
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
230
|
+
5. Open a Pull Request
|
|
231
|
+
|
|
232
|
+
## License
|
|
233
|
+
|
|
234
|
+
MIT
|
|
235
|
+
|
|
236
|
+
## Author
|
|
237
|
+
|
|
238
|
+
**Nurul Islam Rimon**
|
|
239
|
+
|
|
240
|
+
- GitHub: [nurulislamrimon](https://github.com/nurulislamrimon)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ExceptionFilter, ArgumentsHost, DynamicModule } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
interface ErrorMessage {
|
|
4
|
+
path: string;
|
|
5
|
+
message: string;
|
|
6
|
+
}
|
|
7
|
+
interface StandardErrorResponse {
|
|
8
|
+
success: boolean;
|
|
9
|
+
message: string;
|
|
10
|
+
errorMessages: ErrorMessage[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ExceptionFormatter {
|
|
14
|
+
supports(exception: unknown): boolean;
|
|
15
|
+
format(exception: unknown): ErrorMessage[];
|
|
16
|
+
message(exception: unknown): string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ExceptionHandlerConfig {
|
|
20
|
+
enableLogging?: boolean;
|
|
21
|
+
hideStackTrace?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare const DEFAULT_ERROR_MESSAGES: {
|
|
25
|
+
UNKNOWN_ERROR: string;
|
|
26
|
+
VALIDATION_ERROR: string;
|
|
27
|
+
DATABASE_ERROR: string;
|
|
28
|
+
NOT_FOUND: string;
|
|
29
|
+
UNAUTHORIZED: string;
|
|
30
|
+
FORBIDDEN: string;
|
|
31
|
+
CONFLICT: string;
|
|
32
|
+
BAD_REQUEST: string;
|
|
33
|
+
};
|
|
34
|
+
declare const PRISMA_ERROR_MESSAGES: Record<string, string>;
|
|
35
|
+
declare const DEFAULT_PATH = "unknown";
|
|
36
|
+
|
|
37
|
+
declare function isPrismaError(exception: unknown): boolean;
|
|
38
|
+
|
|
39
|
+
declare class PrismaExceptionFormatter implements ExceptionFormatter {
|
|
40
|
+
supports(exception: unknown): boolean;
|
|
41
|
+
format(exception: unknown): ErrorMessage[];
|
|
42
|
+
message(exception: unknown): string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
declare class DtoExceptionFormatter implements ExceptionFormatter {
|
|
46
|
+
supports(exception: unknown): boolean;
|
|
47
|
+
format(exception: unknown): ErrorMessage[];
|
|
48
|
+
message(exception: unknown): string;
|
|
49
|
+
private formatValidationErrors;
|
|
50
|
+
private formatChildrenErrors;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare class HttpExceptionFormatter implements ExceptionFormatter {
|
|
54
|
+
supports(exception: unknown): boolean;
|
|
55
|
+
format(exception: unknown): ErrorMessage[];
|
|
56
|
+
message(exception: unknown): string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare class UnknownExceptionFormatter implements ExceptionFormatter {
|
|
60
|
+
supports(_exception: unknown): boolean;
|
|
61
|
+
format(_exception: unknown): ErrorMessage[];
|
|
62
|
+
message(_exception: unknown): string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
declare class ExceptionHandlerService {
|
|
66
|
+
private formatters;
|
|
67
|
+
private defaultFormatter;
|
|
68
|
+
constructor();
|
|
69
|
+
registerFormatter(formatter: ExceptionFormatter): void;
|
|
70
|
+
getFormatter(exception: unknown): ExceptionFormatter;
|
|
71
|
+
formatException(exception: unknown): {
|
|
72
|
+
errors: ErrorMessage[];
|
|
73
|
+
message: string;
|
|
74
|
+
};
|
|
75
|
+
formatErrors(exception: unknown): ErrorMessage[];
|
|
76
|
+
getErrorMessage(exception: unknown): string;
|
|
77
|
+
getAllFormatters(): ExceptionFormatter[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
declare class GlobalExceptionFilter implements ExceptionFilter {
|
|
81
|
+
private exceptionHandlerService;
|
|
82
|
+
private readonly logger;
|
|
83
|
+
private config;
|
|
84
|
+
constructor(exceptionHandlerService: ExceptionHandlerService, config?: ExceptionHandlerConfig);
|
|
85
|
+
catch(exception: unknown, host: ArgumentsHost): void;
|
|
86
|
+
private getStatusCode;
|
|
87
|
+
private logError;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
declare class ExceptionHandlerModule {
|
|
91
|
+
static forRoot(config?: ExceptionHandlerConfig): DynamicModule;
|
|
92
|
+
static forFeature(config?: ExceptionHandlerConfig): DynamicModule;
|
|
93
|
+
}
|
|
94
|
+
declare function initializeFormatters(service: ExceptionHandlerService): void;
|
|
95
|
+
|
|
96
|
+
export { DEFAULT_ERROR_MESSAGES, DEFAULT_PATH, DtoExceptionFormatter, ErrorMessage, ExceptionFormatter, ExceptionHandlerConfig, ExceptionHandlerModule, ExceptionHandlerService, GlobalExceptionFilter, HttpExceptionFormatter, PRISMA_ERROR_MESSAGES, PrismaExceptionFormatter, StandardErrorResponse, UnknownExceptionFormatter, initializeFormatters, isPrismaError };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ExceptionFilter, ArgumentsHost, DynamicModule } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
interface ErrorMessage {
|
|
4
|
+
path: string;
|
|
5
|
+
message: string;
|
|
6
|
+
}
|
|
7
|
+
interface StandardErrorResponse {
|
|
8
|
+
success: boolean;
|
|
9
|
+
message: string;
|
|
10
|
+
errorMessages: ErrorMessage[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface ExceptionFormatter {
|
|
14
|
+
supports(exception: unknown): boolean;
|
|
15
|
+
format(exception: unknown): ErrorMessage[];
|
|
16
|
+
message(exception: unknown): string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ExceptionHandlerConfig {
|
|
20
|
+
enableLogging?: boolean;
|
|
21
|
+
hideStackTrace?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare const DEFAULT_ERROR_MESSAGES: {
|
|
25
|
+
UNKNOWN_ERROR: string;
|
|
26
|
+
VALIDATION_ERROR: string;
|
|
27
|
+
DATABASE_ERROR: string;
|
|
28
|
+
NOT_FOUND: string;
|
|
29
|
+
UNAUTHORIZED: string;
|
|
30
|
+
FORBIDDEN: string;
|
|
31
|
+
CONFLICT: string;
|
|
32
|
+
BAD_REQUEST: string;
|
|
33
|
+
};
|
|
34
|
+
declare const PRISMA_ERROR_MESSAGES: Record<string, string>;
|
|
35
|
+
declare const DEFAULT_PATH = "unknown";
|
|
36
|
+
|
|
37
|
+
declare function isPrismaError(exception: unknown): boolean;
|
|
38
|
+
|
|
39
|
+
declare class PrismaExceptionFormatter implements ExceptionFormatter {
|
|
40
|
+
supports(exception: unknown): boolean;
|
|
41
|
+
format(exception: unknown): ErrorMessage[];
|
|
42
|
+
message(exception: unknown): string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
declare class DtoExceptionFormatter implements ExceptionFormatter {
|
|
46
|
+
supports(exception: unknown): boolean;
|
|
47
|
+
format(exception: unknown): ErrorMessage[];
|
|
48
|
+
message(exception: unknown): string;
|
|
49
|
+
private formatValidationErrors;
|
|
50
|
+
private formatChildrenErrors;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare class HttpExceptionFormatter implements ExceptionFormatter {
|
|
54
|
+
supports(exception: unknown): boolean;
|
|
55
|
+
format(exception: unknown): ErrorMessage[];
|
|
56
|
+
message(exception: unknown): string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare class UnknownExceptionFormatter implements ExceptionFormatter {
|
|
60
|
+
supports(_exception: unknown): boolean;
|
|
61
|
+
format(_exception: unknown): ErrorMessage[];
|
|
62
|
+
message(_exception: unknown): string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
declare class ExceptionHandlerService {
|
|
66
|
+
private formatters;
|
|
67
|
+
private defaultFormatter;
|
|
68
|
+
constructor();
|
|
69
|
+
registerFormatter(formatter: ExceptionFormatter): void;
|
|
70
|
+
getFormatter(exception: unknown): ExceptionFormatter;
|
|
71
|
+
formatException(exception: unknown): {
|
|
72
|
+
errors: ErrorMessage[];
|
|
73
|
+
message: string;
|
|
74
|
+
};
|
|
75
|
+
formatErrors(exception: unknown): ErrorMessage[];
|
|
76
|
+
getErrorMessage(exception: unknown): string;
|
|
77
|
+
getAllFormatters(): ExceptionFormatter[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
declare class GlobalExceptionFilter implements ExceptionFilter {
|
|
81
|
+
private exceptionHandlerService;
|
|
82
|
+
private readonly logger;
|
|
83
|
+
private config;
|
|
84
|
+
constructor(exceptionHandlerService: ExceptionHandlerService, config?: ExceptionHandlerConfig);
|
|
85
|
+
catch(exception: unknown, host: ArgumentsHost): void;
|
|
86
|
+
private getStatusCode;
|
|
87
|
+
private logError;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
declare class ExceptionHandlerModule {
|
|
91
|
+
static forRoot(config?: ExceptionHandlerConfig): DynamicModule;
|
|
92
|
+
static forFeature(config?: ExceptionHandlerConfig): DynamicModule;
|
|
93
|
+
}
|
|
94
|
+
declare function initializeFormatters(service: ExceptionHandlerService): void;
|
|
95
|
+
|
|
96
|
+
export { DEFAULT_ERROR_MESSAGES, DEFAULT_PATH, DtoExceptionFormatter, ErrorMessage, ExceptionFormatter, ExceptionHandlerConfig, ExceptionHandlerModule, ExceptionHandlerService, GlobalExceptionFilter, HttpExceptionFormatter, PRISMA_ERROR_MESSAGES, PrismaExceptionFormatter, StandardErrorResponse, UnknownExceptionFormatter, initializeFormatters, isPrismaError };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
20
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
21
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
22
|
+
if (decorator = decorators[i])
|
|
23
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
24
|
+
if (kind && result)
|
|
25
|
+
__defProp(target, key, result);
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/index.ts
|
|
30
|
+
var src_exports = {};
|
|
31
|
+
__export(src_exports, {
|
|
32
|
+
DEFAULT_ERROR_MESSAGES: () => DEFAULT_ERROR_MESSAGES,
|
|
33
|
+
DEFAULT_PATH: () => DEFAULT_PATH,
|
|
34
|
+
DtoExceptionFormatter: () => DtoExceptionFormatter,
|
|
35
|
+
ExceptionHandlerModule: () => ExceptionHandlerModule,
|
|
36
|
+
ExceptionHandlerService: () => ExceptionHandlerService,
|
|
37
|
+
GlobalExceptionFilter: () => GlobalExceptionFilter,
|
|
38
|
+
HttpExceptionFormatter: () => HttpExceptionFormatter,
|
|
39
|
+
PRISMA_ERROR_MESSAGES: () => PRISMA_ERROR_MESSAGES,
|
|
40
|
+
PrismaExceptionFormatter: () => PrismaExceptionFormatter,
|
|
41
|
+
UnknownExceptionFormatter: () => UnknownExceptionFormatter,
|
|
42
|
+
initializeFormatters: () => initializeFormatters,
|
|
43
|
+
isPrismaError: () => isPrismaError
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(src_exports);
|
|
46
|
+
|
|
47
|
+
// src/constants/default-messages.ts
|
|
48
|
+
var DEFAULT_ERROR_MESSAGES = {
|
|
49
|
+
UNKNOWN_ERROR: "An unexpected error occurred",
|
|
50
|
+
VALIDATION_ERROR: "Validation failed",
|
|
51
|
+
DATABASE_ERROR: "Database error",
|
|
52
|
+
NOT_FOUND: "Resource not found",
|
|
53
|
+
UNAUTHORIZED: "Unauthorized access",
|
|
54
|
+
FORBIDDEN: "Access forbidden",
|
|
55
|
+
CONFLICT: "Conflict occurred",
|
|
56
|
+
BAD_REQUEST: "Bad request"
|
|
57
|
+
};
|
|
58
|
+
var PRISMA_ERROR_MESSAGES = {
|
|
59
|
+
P2002: "A record with this {field} already exists.",
|
|
60
|
+
P2003: "This {field} does not exist.",
|
|
61
|
+
P2005: "Invalid value for {field}.",
|
|
62
|
+
P2006: "Invalid format for {field}.",
|
|
63
|
+
P2025: "Record not found."
|
|
64
|
+
};
|
|
65
|
+
var DEFAULT_PATH = "unknown";
|
|
66
|
+
|
|
67
|
+
// src/utils/is-prisma-error.ts
|
|
68
|
+
function isPrismaError(exception) {
|
|
69
|
+
if (!exception || typeof exception !== "object") {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const error = exception;
|
|
73
|
+
const hasErrorCode = typeof error.code === "string";
|
|
74
|
+
const hasClientVersion = typeof error.clientVersion === "string";
|
|
75
|
+
const hasMeta = typeof error.meta === "object";
|
|
76
|
+
return hasErrorCode && (hasClientVersion || hasMeta);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/formatters/prisma-exception.formatter.ts
|
|
80
|
+
var PrismaExceptionFormatter = class {
|
|
81
|
+
supports(exception) {
|
|
82
|
+
return isPrismaError(exception);
|
|
83
|
+
}
|
|
84
|
+
format(exception) {
|
|
85
|
+
const error = exception;
|
|
86
|
+
const code = error.code;
|
|
87
|
+
const meta = error.meta;
|
|
88
|
+
const target = error.target;
|
|
89
|
+
const messageTemplate = PRISMA_ERROR_MESSAGES[code] || "Database operation failed";
|
|
90
|
+
let path = "unknown";
|
|
91
|
+
let message = messageTemplate;
|
|
92
|
+
if (code === "P2002" && target && target.length > 0) {
|
|
93
|
+
path = target[0];
|
|
94
|
+
message = messageTemplate.replace("{field}", path);
|
|
95
|
+
} else if (code === "P2003" && meta?.field_name) {
|
|
96
|
+
path = meta.field_name;
|
|
97
|
+
message = messageTemplate.replace("{field}", path);
|
|
98
|
+
} else if (code === "P2005" && meta?.field_name) {
|
|
99
|
+
path = meta.field_name;
|
|
100
|
+
message = messageTemplate.replace("{field}", path);
|
|
101
|
+
} else if (code === "P2006" && meta?.field_name) {
|
|
102
|
+
path = meta.field_name;
|
|
103
|
+
message = messageTemplate.replace("{field}", path);
|
|
104
|
+
} else if (code === "P2025") {
|
|
105
|
+
path = "record";
|
|
106
|
+
message = messageTemplate;
|
|
107
|
+
}
|
|
108
|
+
return [{ path, message }];
|
|
109
|
+
}
|
|
110
|
+
message(exception) {
|
|
111
|
+
const error = exception;
|
|
112
|
+
const code = error.code;
|
|
113
|
+
return PRISMA_ERROR_MESSAGES[code] || "Database error";
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// src/formatters/dto-exception.formatter.ts
|
|
118
|
+
var DtoExceptionFormatter = class {
|
|
119
|
+
supports(exception) {
|
|
120
|
+
if (!exception || typeof exception !== "object") {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
const error = exception;
|
|
124
|
+
return Array.isArray(error.validationErrors) || Array.isArray(error.children) && error.constraints !== void 0;
|
|
125
|
+
}
|
|
126
|
+
format(exception) {
|
|
127
|
+
const error = exception;
|
|
128
|
+
const validationErrors = error.validationErrors;
|
|
129
|
+
const children = error.children;
|
|
130
|
+
if (validationErrors) {
|
|
131
|
+
return this.formatValidationErrors(validationErrors);
|
|
132
|
+
}
|
|
133
|
+
if (children) {
|
|
134
|
+
return this.formatChildrenErrors(children);
|
|
135
|
+
}
|
|
136
|
+
return [{ path: "unknown", message: "Validation failed" }];
|
|
137
|
+
}
|
|
138
|
+
message(exception) {
|
|
139
|
+
const error = exception;
|
|
140
|
+
const validationErrors = error.validationErrors;
|
|
141
|
+
const children = error.children;
|
|
142
|
+
if (validationErrors && validationErrors.length > 0) {
|
|
143
|
+
const firstError = validationErrors[0];
|
|
144
|
+
const constraints = firstError.constraints;
|
|
145
|
+
if (constraints) {
|
|
146
|
+
return Object.values(constraints)[0];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (children && children.length > 0) {
|
|
150
|
+
return "Validation failed for nested fields";
|
|
151
|
+
}
|
|
152
|
+
return "Validation failed";
|
|
153
|
+
}
|
|
154
|
+
formatValidationErrors(errors) {
|
|
155
|
+
return errors.flatMap((error) => {
|
|
156
|
+
const constraints = error.constraints;
|
|
157
|
+
if (constraints) {
|
|
158
|
+
return Object.entries(constraints).map(([, value]) => ({
|
|
159
|
+
path: error.property,
|
|
160
|
+
message: value
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
163
|
+
return [];
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
formatChildrenErrors(children) {
|
|
167
|
+
return children.flatMap((child) => {
|
|
168
|
+
const constraints = child.constraints;
|
|
169
|
+
if (constraints) {
|
|
170
|
+
return Object.entries(constraints).map(([, value]) => ({
|
|
171
|
+
path: child.property,
|
|
172
|
+
message: value
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
175
|
+
return [];
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// src/formatters/http-exception.formatter.ts
|
|
181
|
+
var import_common = require("@nestjs/common");
|
|
182
|
+
var HttpExceptionFormatter = class {
|
|
183
|
+
supports(exception) {
|
|
184
|
+
return exception instanceof import_common.HttpException;
|
|
185
|
+
}
|
|
186
|
+
format(exception) {
|
|
187
|
+
const httpException = exception;
|
|
188
|
+
const response = httpException.getResponse();
|
|
189
|
+
if (typeof response === "string") {
|
|
190
|
+
return [{ path: "unknown", message: response }];
|
|
191
|
+
}
|
|
192
|
+
if (typeof response === "object" && response !== null) {
|
|
193
|
+
const responseObj = response;
|
|
194
|
+
if (responseObj.message && Array.isArray(responseObj.message)) {
|
|
195
|
+
return responseObj.message.map((msg) => ({
|
|
196
|
+
path: "unknown",
|
|
197
|
+
message: msg
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
if (responseObj.message && typeof responseObj.message === "string") {
|
|
201
|
+
return [{ path: "unknown", message: responseObj.message }];
|
|
202
|
+
}
|
|
203
|
+
if (responseObj.error && typeof responseObj.error === "string") {
|
|
204
|
+
return [{ path: "unknown", message: responseObj.error }];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return [{ path: "unknown", message: "An error occurred" }];
|
|
208
|
+
}
|
|
209
|
+
message(exception) {
|
|
210
|
+
const httpException = exception;
|
|
211
|
+
const response = httpException.getResponse();
|
|
212
|
+
if (typeof response === "string") {
|
|
213
|
+
return response;
|
|
214
|
+
}
|
|
215
|
+
if (typeof response === "object" && response !== null) {
|
|
216
|
+
const responseObj = response;
|
|
217
|
+
if (responseObj.message && typeof responseObj.message === "string") {
|
|
218
|
+
return responseObj.message;
|
|
219
|
+
}
|
|
220
|
+
if (responseObj.error && typeof responseObj.error === "string") {
|
|
221
|
+
return responseObj.error;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return "An error occurred";
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// src/formatters/unknown-exception.formatter.ts
|
|
229
|
+
var UnknownExceptionFormatter = class {
|
|
230
|
+
supports(_exception) {
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
format(_exception) {
|
|
234
|
+
return [
|
|
235
|
+
{
|
|
236
|
+
path: DEFAULT_PATH,
|
|
237
|
+
message: "Internal server error"
|
|
238
|
+
}
|
|
239
|
+
];
|
|
240
|
+
}
|
|
241
|
+
message(_exception) {
|
|
242
|
+
return "Internal server error";
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// src/services/exception-handler.service.ts
|
|
247
|
+
var import_common2 = require("@nestjs/common");
|
|
248
|
+
var ExceptionHandlerService = class {
|
|
249
|
+
constructor() {
|
|
250
|
+
this.formatters = [];
|
|
251
|
+
this.defaultFormatter = new UnknownExceptionFormatter();
|
|
252
|
+
}
|
|
253
|
+
registerFormatter(formatter) {
|
|
254
|
+
this.formatters.push(formatter);
|
|
255
|
+
}
|
|
256
|
+
getFormatter(exception) {
|
|
257
|
+
for (const formatter of this.formatters) {
|
|
258
|
+
if (formatter.supports(exception)) {
|
|
259
|
+
return formatter;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return this.defaultFormatter;
|
|
263
|
+
}
|
|
264
|
+
formatException(exception) {
|
|
265
|
+
const formatter = this.getFormatter(exception);
|
|
266
|
+
const errors = formatter.format(exception);
|
|
267
|
+
const message = formatter.message(exception);
|
|
268
|
+
return { errors, message };
|
|
269
|
+
}
|
|
270
|
+
formatErrors(exception) {
|
|
271
|
+
const formatter = this.getFormatter(exception);
|
|
272
|
+
return formatter.format(exception);
|
|
273
|
+
}
|
|
274
|
+
getErrorMessage(exception) {
|
|
275
|
+
const formatter = this.getFormatter(exception);
|
|
276
|
+
return formatter.message(exception);
|
|
277
|
+
}
|
|
278
|
+
getAllFormatters() {
|
|
279
|
+
return [...this.formatters];
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
ExceptionHandlerService = __decorateClass([
|
|
283
|
+
(0, import_common2.Injectable)()
|
|
284
|
+
], ExceptionHandlerService);
|
|
285
|
+
|
|
286
|
+
// src/filter/global-exception.filter.ts
|
|
287
|
+
var import_common3 = require("@nestjs/common");
|
|
288
|
+
var GlobalExceptionFilter = class {
|
|
289
|
+
constructor(exceptionHandlerService, config) {
|
|
290
|
+
this.exceptionHandlerService = exceptionHandlerService;
|
|
291
|
+
this.logger = new import_common3.Logger(GlobalExceptionFilter.name);
|
|
292
|
+
this.config = config || { enableLogging: true, hideStackTrace: false };
|
|
293
|
+
}
|
|
294
|
+
catch(exception, host) {
|
|
295
|
+
const ctx = host.switchToHttp();
|
|
296
|
+
const response = ctx.getResponse();
|
|
297
|
+
const request = ctx.getRequest();
|
|
298
|
+
const { errors, message } = this.exceptionHandlerService.formatException(exception);
|
|
299
|
+
const status = this.getStatusCode(exception);
|
|
300
|
+
const errorResponse = {
|
|
301
|
+
success: false,
|
|
302
|
+
message,
|
|
303
|
+
errorMessages: errors
|
|
304
|
+
};
|
|
305
|
+
if (this.config.enableLogging) {
|
|
306
|
+
this.logError(request, status, errors, exception);
|
|
307
|
+
}
|
|
308
|
+
response.status(status).json(errorResponse);
|
|
309
|
+
}
|
|
310
|
+
getStatusCode(exception) {
|
|
311
|
+
if (exception instanceof import_common3.HttpException) {
|
|
312
|
+
return exception.getStatus();
|
|
313
|
+
}
|
|
314
|
+
return import_common3.HttpStatus.INTERNAL_SERVER_ERROR;
|
|
315
|
+
}
|
|
316
|
+
logError(request, status, errorMessages, exception) {
|
|
317
|
+
const method = request.method;
|
|
318
|
+
const url = request.url;
|
|
319
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
320
|
+
const logData = {
|
|
321
|
+
timestamp,
|
|
322
|
+
method,
|
|
323
|
+
url,
|
|
324
|
+
status,
|
|
325
|
+
errorMessages,
|
|
326
|
+
stack: this.config.hideStackTrace ? void 0 : exception?.stack
|
|
327
|
+
};
|
|
328
|
+
this.logger.error(`${method} ${url} - ${status}`, JSON.stringify(logData));
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
GlobalExceptionFilter = __decorateClass([
|
|
332
|
+
(0, import_common3.Catch)()
|
|
333
|
+
], GlobalExceptionFilter);
|
|
334
|
+
|
|
335
|
+
// src/module/exception-handler.module.ts
|
|
336
|
+
var import_common4 = require("@nestjs/common");
|
|
337
|
+
var ExceptionHandlerModule = class {
|
|
338
|
+
static forRoot(config) {
|
|
339
|
+
const providers = [
|
|
340
|
+
ExceptionHandlerService,
|
|
341
|
+
{
|
|
342
|
+
provide: GlobalExceptionFilter,
|
|
343
|
+
useFactory: (service) => {
|
|
344
|
+
return new GlobalExceptionFilter(service, config);
|
|
345
|
+
},
|
|
346
|
+
inject: [ExceptionHandlerService]
|
|
347
|
+
}
|
|
348
|
+
];
|
|
349
|
+
return {
|
|
350
|
+
module: ExceptionHandlerModule,
|
|
351
|
+
providers,
|
|
352
|
+
exports: [ExceptionHandlerService, GlobalExceptionFilter],
|
|
353
|
+
global: true
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
static forFeature(config) {
|
|
357
|
+
return this.forRoot(config);
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
ExceptionHandlerModule = __decorateClass([
|
|
361
|
+
(0, import_common4.Module)({})
|
|
362
|
+
], ExceptionHandlerModule);
|
|
363
|
+
function initializeFormatters(service) {
|
|
364
|
+
service.registerFormatter(new PrismaExceptionFormatter());
|
|
365
|
+
service.registerFormatter(new DtoExceptionFormatter());
|
|
366
|
+
service.registerFormatter(new HttpExceptionFormatter());
|
|
367
|
+
service.registerFormatter(new UnknownExceptionFormatter());
|
|
368
|
+
}
|
|
369
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
370
|
+
0 && (module.exports = {
|
|
371
|
+
DEFAULT_ERROR_MESSAGES,
|
|
372
|
+
DEFAULT_PATH,
|
|
373
|
+
DtoExceptionFormatter,
|
|
374
|
+
ExceptionHandlerModule,
|
|
375
|
+
ExceptionHandlerService,
|
|
376
|
+
GlobalExceptionFilter,
|
|
377
|
+
HttpExceptionFormatter,
|
|
378
|
+
PRISMA_ERROR_MESSAGES,
|
|
379
|
+
PrismaExceptionFormatter,
|
|
380
|
+
UnknownExceptionFormatter,
|
|
381
|
+
initializeFormatters,
|
|
382
|
+
isPrismaError
|
|
383
|
+
});
|
|
384
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/constants/default-messages.ts","../src/utils/is-prisma-error.ts","../src/formatters/prisma-exception.formatter.ts","../src/formatters/dto-exception.formatter.ts","../src/formatters/http-exception.formatter.ts","../src/formatters/unknown-exception.formatter.ts","../src/services/exception-handler.service.ts","../src/filter/global-exception.filter.ts","../src/module/exception-handler.module.ts"],"sourcesContent":["// Interfaces\nexport * from './interfaces';\n\n// Constants\nexport * from './constants';\n\n// Utilities\nexport * from './utils/is-prisma-error';\n\n// Formatters\nexport * from './formatters';\n\n// Services\nexport * from './services/exception-handler.service';\n\n// Filters\nexport * from './filter/global-exception.filter';\n\n// Module\nexport * from './module/exception-handler.module';\n","export const DEFAULT_ERROR_MESSAGES = {\n UNKNOWN_ERROR: 'An unexpected error occurred',\n VALIDATION_ERROR: 'Validation failed',\n DATABASE_ERROR: 'Database error',\n NOT_FOUND: 'Resource not found',\n UNAUTHORIZED: 'Unauthorized access',\n FORBIDDEN: 'Access forbidden',\n CONFLICT: 'Conflict occurred',\n BAD_REQUEST: 'Bad request',\n};\n\nexport const PRISMA_ERROR_MESSAGES: Record<string, string> = {\n P2002: 'A record with this {field} already exists.',\n P2003: 'This {field} does not exist.',\n P2005: 'Invalid value for {field}.',\n P2006: 'Invalid format for {field}.',\n P2025: 'Record not found.',\n};\n\nexport const DEFAULT_PATH = 'unknown';\n","export function isPrismaError(exception: unknown): boolean {\n if (!exception || typeof exception !== 'object') {\n return false;\n }\n\n const error = exception as Record<string, unknown>;\n\n // Check for Prisma error characteristics\n const hasErrorCode = typeof error.code === 'string';\n const hasClientVersion = typeof error.clientVersion === 'string';\n const hasMeta = typeof error.meta === 'object';\n\n // Prisma errors typically have these properties\n return hasErrorCode && (hasClientVersion || hasMeta);\n}\n","import { ExceptionFormatter } from '../interfaces/exception-formatter.interface';\nimport { ErrorMessage } from '../interfaces/error-message.interface';\nimport { isPrismaError } from '../utils/is-prisma-error';\nimport { PRISMA_ERROR_MESSAGES } from '../constants/default-messages';\n\nexport class PrismaExceptionFormatter implements ExceptionFormatter {\n supports(exception: unknown): boolean {\n return isPrismaError(exception);\n }\n\n format(exception: unknown): ErrorMessage[] {\n const error = exception as Record<string, unknown>;\n const code = error.code as string;\n const meta = error.meta as Record<string, string> | undefined;\n const target = error.target as string[] | undefined;\n\n const messageTemplate = PRISMA_ERROR_MESSAGES[code] || 'Database operation failed';\n let path = 'unknown';\n let message = messageTemplate;\n\n if (code === 'P2002' && target && target.length > 0) {\n path = target[0];\n message = messageTemplate.replace('{field}', path);\n } else if (code === 'P2003' && meta?.field_name) {\n path = meta.field_name;\n message = messageTemplate.replace('{field}', path);\n } else if (code === 'P2005' && meta?.field_name) {\n path = meta.field_name;\n message = messageTemplate.replace('{field}', path);\n } else if (code === 'P2006' && meta?.field_name) {\n path = meta.field_name;\n message = messageTemplate.replace('{field}', path);\n } else if (code === 'P2025') {\n path = 'record';\n message = messageTemplate;\n }\n\n return [{ path, message }];\n }\n\n message(exception: unknown): string {\n const error = exception as Record<string, unknown>;\n const code = error.code as string;\n return PRISMA_ERROR_MESSAGES[code] || 'Database error';\n }\n}\n","import { ValidationError } from '@nestjs/common';\nimport { ExceptionFormatter } from '../interfaces/exception-formatter.interface';\nimport { ErrorMessage } from '../interfaces/error-message.interface';\n\nexport class DtoExceptionFormatter implements ExceptionFormatter {\n supports(exception: unknown): boolean {\n if (!exception || typeof exception !== 'object') {\n return false;\n }\n\n const error = exception as Record<string, unknown>;\n return (\n Array.isArray(error.validationErrors) ||\n (Array.isArray(error.children) && error.constraints !== undefined)\n );\n }\n\n format(exception: unknown): ErrorMessage[] {\n const error = exception as Record<string, unknown>;\n const validationErrors = error.validationErrors as ValidationError[] | undefined;\n const children = error.children as ValidationError[] | undefined;\n\n if (validationErrors) {\n return this.formatValidationErrors(validationErrors);\n }\n\n if (children) {\n return this.formatChildrenErrors(children);\n }\n\n return [{ path: 'unknown', message: 'Validation failed' }];\n }\n\n message(exception: unknown): string {\n const error = exception as Record<string, unknown>;\n const validationErrors = error.validationErrors as ValidationError[] | undefined;\n const children = error.children as ValidationError[] | undefined;\n\n if (validationErrors && validationErrors.length > 0) {\n const firstError = validationErrors[0];\n const constraints = firstError.constraints;\n if (constraints) {\n return Object.values(constraints)[0];\n }\n }\n\n if (children && children.length > 0) {\n return 'Validation failed for nested fields';\n }\n\n return 'Validation failed';\n }\n\n private formatValidationErrors(errors: ValidationError[]): ErrorMessage[] {\n return errors.flatMap((error) => {\n const constraints = error.constraints;\n if (constraints) {\n return Object.entries(constraints).map(([, value]) => ({\n path: error.property,\n message: value,\n }));\n }\n return [];\n });\n }\n\n private formatChildrenErrors(children: ValidationError[]): ErrorMessage[] {\n return children.flatMap((child) => {\n const constraints = child.constraints;\n if (constraints) {\n return Object.entries(constraints).map(([, value]) => ({\n path: child.property,\n message: value,\n }));\n }\n return [];\n });\n }\n}\n","import { HttpException } from '@nestjs/common';\nimport { ExceptionFormatter } from '../interfaces/exception-formatter.interface';\nimport { ErrorMessage } from '../interfaces/error-message.interface';\n\nexport class HttpExceptionFormatter implements ExceptionFormatter {\n supports(exception: unknown): boolean {\n return exception instanceof HttpException;\n }\n\n format(exception: unknown): ErrorMessage[] {\n const httpException = exception as HttpException;\n const response = httpException.getResponse();\n\n if (typeof response === 'string') {\n return [{ path: 'unknown', message: response }];\n }\n\n if (typeof response === 'object' && response !== null) {\n const responseObj = response as Record<string, unknown>;\n\n if (responseObj.message && Array.isArray(responseObj.message)) {\n // Handle validation errors from class-validator in HTTP exceptions\n return (responseObj.message as string[]).map((msg) => ({\n path: 'unknown',\n message: msg,\n }));\n }\n\n if (responseObj.message && typeof responseObj.message === 'string') {\n return [{ path: 'unknown', message: responseObj.message }];\n }\n\n if (responseObj.error && typeof responseObj.error === 'string') {\n return [{ path: 'unknown', message: responseObj.error }];\n }\n }\n\n return [{ path: 'unknown', message: 'An error occurred' }];\n }\n\n message(exception: unknown): string {\n const httpException = exception as HttpException;\n const response = httpException.getResponse();\n\n if (typeof response === 'string') {\n return response;\n }\n\n if (typeof response === 'object' && response !== null) {\n const responseObj = response as Record<string, unknown>;\n\n if (responseObj.message && typeof responseObj.message === 'string') {\n return responseObj.message;\n }\n\n if (responseObj.error && typeof responseObj.error === 'string') {\n return responseObj.error;\n }\n }\n\n return 'An error occurred';\n }\n}\n","import { ExceptionFormatter } from '../interfaces/exception-formatter.interface';\nimport { ErrorMessage } from '../interfaces/error-message.interface';\nimport { DEFAULT_PATH } from '../constants/default-messages';\n\nexport class UnknownExceptionFormatter implements ExceptionFormatter {\n supports(_exception: unknown): boolean {\n return true;\n }\n\n format(_exception: unknown): ErrorMessage[] {\n return [\n {\n path: DEFAULT_PATH,\n message: 'Internal server error',\n },\n ];\n }\n\n message(_exception: unknown): string {\n return 'Internal server error';\n }\n}\n","import { Injectable } from '@nestjs/common';\nimport { ExceptionFormatter } from '../interfaces/exception-formatter.interface';\nimport { ErrorMessage } from '../interfaces/error-message.interface';\nimport { UnknownExceptionFormatter } from '../formatters/unknown-exception.formatter';\n\n@Injectable()\nexport class ExceptionHandlerService {\n private formatters: ExceptionFormatter[] = [];\n private defaultFormatter: ExceptionFormatter;\n\n constructor() {\n this.defaultFormatter = new UnknownExceptionFormatter();\n }\n\n registerFormatter(formatter: ExceptionFormatter): void {\n this.formatters.push(formatter);\n }\n\n getFormatter(exception: unknown): ExceptionFormatter {\n for (const formatter of this.formatters) {\n if (formatter.supports(exception)) {\n return formatter;\n }\n }\n return this.defaultFormatter;\n }\n\n formatException(exception: unknown): { errors: ErrorMessage[]; message: string } {\n const formatter = this.getFormatter(exception);\n const errors = formatter.format(exception);\n const message = formatter.message(exception);\n\n return { errors, message };\n }\n\n formatErrors(exception: unknown): ErrorMessage[] {\n const formatter = this.getFormatter(exception);\n return formatter.format(exception);\n }\n\n getErrorMessage(exception: unknown): string {\n const formatter = this.getFormatter(exception);\n return formatter.message(exception);\n }\n\n getAllFormatters(): ExceptionFormatter[] {\n return [...this.formatters];\n }\n}\n","import {\n ExceptionFilter,\n Catch,\n ArgumentsHost,\n HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\nimport { Request, Response } from 'express';\nimport { ExceptionHandlerService } from '../services/exception-handler.service';\nimport { StandardErrorResponse } from '../interfaces/error-message.interface';\nimport { ExceptionHandlerConfig } from '../interfaces/exception-handler-config.interface';\n\n@Catch()\nexport class GlobalExceptionFilter implements ExceptionFilter {\n private readonly logger = new Logger(GlobalExceptionFilter.name);\n private config: ExceptionHandlerConfig;\n\n constructor(\n private exceptionHandlerService: ExceptionHandlerService,\n config?: ExceptionHandlerConfig,\n ) {\n this.config = config || { enableLogging: true, hideStackTrace: false };\n }\n\n catch(exception: unknown, host: ArgumentsHost): void {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse<Response>();\n const request = ctx.getRequest<Request>();\n\n const { errors, message } = this.exceptionHandlerService.formatException(exception);\n const status = this.getStatusCode(exception);\n\n const errorResponse: StandardErrorResponse = {\n success: false,\n message,\n errorMessages: errors,\n };\n\n if (this.config.enableLogging) {\n this.logError(request, status, errors, exception);\n }\n\n response.status(status).json(errorResponse);\n }\n\n private getStatusCode(exception: unknown): number {\n if (exception instanceof HttpException) {\n return exception.getStatus();\n }\n return HttpStatus.INTERNAL_SERVER_ERROR;\n }\n\n private logError(\n request: Request,\n status: number,\n errorMessages: { path: string; message: string }[],\n exception: unknown,\n ): void {\n const method = request.method;\n const url = request.url;\n const timestamp = new Date().toISOString();\n\n const logData = {\n timestamp,\n method,\n url,\n status,\n errorMessages,\n stack: this.config.hideStackTrace ? undefined : (exception as Error)?.stack,\n };\n\n this.logger.error(`${method} ${url} - ${status}`, JSON.stringify(logData));\n }\n}\n","import { DynamicModule, Module } from '@nestjs/common';\nimport { ExceptionHandlerService } from '../services/exception-handler.service';\nimport { GlobalExceptionFilter } from '../filter/global-exception.filter';\nimport { ExceptionHandlerConfig } from '../interfaces/exception-handler-config.interface';\nimport { PrismaExceptionFormatter } from '../formatters/prisma-exception.formatter';\nimport { DtoExceptionFormatter } from '../formatters/dto-exception.formatter';\nimport { HttpExceptionFormatter } from '../formatters/http-exception.formatter';\nimport { UnknownExceptionFormatter } from '../formatters/unknown-exception.formatter';\n\n@Module({})\nexport class ExceptionHandlerModule {\n static forRoot(config?: ExceptionHandlerConfig): DynamicModule {\n const providers = [\n ExceptionHandlerService,\n {\n provide: GlobalExceptionFilter,\n useFactory: (service: ExceptionHandlerService) => {\n return new GlobalExceptionFilter(service, config);\n },\n inject: [ExceptionHandlerService],\n },\n ];\n\n return {\n module: ExceptionHandlerModule,\n providers,\n exports: [ExceptionHandlerService, GlobalExceptionFilter],\n global: true,\n };\n }\n\n static forFeature(config?: ExceptionHandlerConfig): DynamicModule {\n return this.forRoot(config);\n }\n}\n\n// Initialize formatters\nexport function initializeFormatters(service: ExceptionHandlerService): void {\n service.registerFormatter(new PrismaExceptionFormatter());\n service.registerFormatter(new DtoExceptionFormatter());\n service.registerFormatter(new HttpExceptionFormatter());\n service.registerFormatter(new UnknownExceptionFormatter());\n}\n"],"mappings":"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","names":["import_common","import_common","import_common"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result)
|
|
9
|
+
__defProp(target, key, result);
|
|
10
|
+
return result;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/constants/default-messages.ts
|
|
14
|
+
var DEFAULT_ERROR_MESSAGES = {
|
|
15
|
+
UNKNOWN_ERROR: "An unexpected error occurred",
|
|
16
|
+
VALIDATION_ERROR: "Validation failed",
|
|
17
|
+
DATABASE_ERROR: "Database error",
|
|
18
|
+
NOT_FOUND: "Resource not found",
|
|
19
|
+
UNAUTHORIZED: "Unauthorized access",
|
|
20
|
+
FORBIDDEN: "Access forbidden",
|
|
21
|
+
CONFLICT: "Conflict occurred",
|
|
22
|
+
BAD_REQUEST: "Bad request"
|
|
23
|
+
};
|
|
24
|
+
var PRISMA_ERROR_MESSAGES = {
|
|
25
|
+
P2002: "A record with this {field} already exists.",
|
|
26
|
+
P2003: "This {field} does not exist.",
|
|
27
|
+
P2005: "Invalid value for {field}.",
|
|
28
|
+
P2006: "Invalid format for {field}.",
|
|
29
|
+
P2025: "Record not found."
|
|
30
|
+
};
|
|
31
|
+
var DEFAULT_PATH = "unknown";
|
|
32
|
+
|
|
33
|
+
// src/utils/is-prisma-error.ts
|
|
34
|
+
function isPrismaError(exception) {
|
|
35
|
+
if (!exception || typeof exception !== "object") {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const error = exception;
|
|
39
|
+
const hasErrorCode = typeof error.code === "string";
|
|
40
|
+
const hasClientVersion = typeof error.clientVersion === "string";
|
|
41
|
+
const hasMeta = typeof error.meta === "object";
|
|
42
|
+
return hasErrorCode && (hasClientVersion || hasMeta);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/formatters/prisma-exception.formatter.ts
|
|
46
|
+
var PrismaExceptionFormatter = class {
|
|
47
|
+
supports(exception) {
|
|
48
|
+
return isPrismaError(exception);
|
|
49
|
+
}
|
|
50
|
+
format(exception) {
|
|
51
|
+
const error = exception;
|
|
52
|
+
const code = error.code;
|
|
53
|
+
const meta = error.meta;
|
|
54
|
+
const target = error.target;
|
|
55
|
+
const messageTemplate = PRISMA_ERROR_MESSAGES[code] || "Database operation failed";
|
|
56
|
+
let path = "unknown";
|
|
57
|
+
let message = messageTemplate;
|
|
58
|
+
if (code === "P2002" && target && target.length > 0) {
|
|
59
|
+
path = target[0];
|
|
60
|
+
message = messageTemplate.replace("{field}", path);
|
|
61
|
+
} else if (code === "P2003" && meta?.field_name) {
|
|
62
|
+
path = meta.field_name;
|
|
63
|
+
message = messageTemplate.replace("{field}", path);
|
|
64
|
+
} else if (code === "P2005" && meta?.field_name) {
|
|
65
|
+
path = meta.field_name;
|
|
66
|
+
message = messageTemplate.replace("{field}", path);
|
|
67
|
+
} else if (code === "P2006" && meta?.field_name) {
|
|
68
|
+
path = meta.field_name;
|
|
69
|
+
message = messageTemplate.replace("{field}", path);
|
|
70
|
+
} else if (code === "P2025") {
|
|
71
|
+
path = "record";
|
|
72
|
+
message = messageTemplate;
|
|
73
|
+
}
|
|
74
|
+
return [{ path, message }];
|
|
75
|
+
}
|
|
76
|
+
message(exception) {
|
|
77
|
+
const error = exception;
|
|
78
|
+
const code = error.code;
|
|
79
|
+
return PRISMA_ERROR_MESSAGES[code] || "Database error";
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// src/formatters/dto-exception.formatter.ts
|
|
84
|
+
var DtoExceptionFormatter = class {
|
|
85
|
+
supports(exception) {
|
|
86
|
+
if (!exception || typeof exception !== "object") {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const error = exception;
|
|
90
|
+
return Array.isArray(error.validationErrors) || Array.isArray(error.children) && error.constraints !== void 0;
|
|
91
|
+
}
|
|
92
|
+
format(exception) {
|
|
93
|
+
const error = exception;
|
|
94
|
+
const validationErrors = error.validationErrors;
|
|
95
|
+
const children = error.children;
|
|
96
|
+
if (validationErrors) {
|
|
97
|
+
return this.formatValidationErrors(validationErrors);
|
|
98
|
+
}
|
|
99
|
+
if (children) {
|
|
100
|
+
return this.formatChildrenErrors(children);
|
|
101
|
+
}
|
|
102
|
+
return [{ path: "unknown", message: "Validation failed" }];
|
|
103
|
+
}
|
|
104
|
+
message(exception) {
|
|
105
|
+
const error = exception;
|
|
106
|
+
const validationErrors = error.validationErrors;
|
|
107
|
+
const children = error.children;
|
|
108
|
+
if (validationErrors && validationErrors.length > 0) {
|
|
109
|
+
const firstError = validationErrors[0];
|
|
110
|
+
const constraints = firstError.constraints;
|
|
111
|
+
if (constraints) {
|
|
112
|
+
return Object.values(constraints)[0];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (children && children.length > 0) {
|
|
116
|
+
return "Validation failed for nested fields";
|
|
117
|
+
}
|
|
118
|
+
return "Validation failed";
|
|
119
|
+
}
|
|
120
|
+
formatValidationErrors(errors) {
|
|
121
|
+
return errors.flatMap((error) => {
|
|
122
|
+
const constraints = error.constraints;
|
|
123
|
+
if (constraints) {
|
|
124
|
+
return Object.entries(constraints).map(([, value]) => ({
|
|
125
|
+
path: error.property,
|
|
126
|
+
message: value
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
return [];
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
formatChildrenErrors(children) {
|
|
133
|
+
return children.flatMap((child) => {
|
|
134
|
+
const constraints = child.constraints;
|
|
135
|
+
if (constraints) {
|
|
136
|
+
return Object.entries(constraints).map(([, value]) => ({
|
|
137
|
+
path: child.property,
|
|
138
|
+
message: value
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
return [];
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// src/formatters/http-exception.formatter.ts
|
|
147
|
+
import { HttpException } from "@nestjs/common";
|
|
148
|
+
var HttpExceptionFormatter = class {
|
|
149
|
+
supports(exception) {
|
|
150
|
+
return exception instanceof HttpException;
|
|
151
|
+
}
|
|
152
|
+
format(exception) {
|
|
153
|
+
const httpException = exception;
|
|
154
|
+
const response = httpException.getResponse();
|
|
155
|
+
if (typeof response === "string") {
|
|
156
|
+
return [{ path: "unknown", message: response }];
|
|
157
|
+
}
|
|
158
|
+
if (typeof response === "object" && response !== null) {
|
|
159
|
+
const responseObj = response;
|
|
160
|
+
if (responseObj.message && Array.isArray(responseObj.message)) {
|
|
161
|
+
return responseObj.message.map((msg) => ({
|
|
162
|
+
path: "unknown",
|
|
163
|
+
message: msg
|
|
164
|
+
}));
|
|
165
|
+
}
|
|
166
|
+
if (responseObj.message && typeof responseObj.message === "string") {
|
|
167
|
+
return [{ path: "unknown", message: responseObj.message }];
|
|
168
|
+
}
|
|
169
|
+
if (responseObj.error && typeof responseObj.error === "string") {
|
|
170
|
+
return [{ path: "unknown", message: responseObj.error }];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return [{ path: "unknown", message: "An error occurred" }];
|
|
174
|
+
}
|
|
175
|
+
message(exception) {
|
|
176
|
+
const httpException = exception;
|
|
177
|
+
const response = httpException.getResponse();
|
|
178
|
+
if (typeof response === "string") {
|
|
179
|
+
return response;
|
|
180
|
+
}
|
|
181
|
+
if (typeof response === "object" && response !== null) {
|
|
182
|
+
const responseObj = response;
|
|
183
|
+
if (responseObj.message && typeof responseObj.message === "string") {
|
|
184
|
+
return responseObj.message;
|
|
185
|
+
}
|
|
186
|
+
if (responseObj.error && typeof responseObj.error === "string") {
|
|
187
|
+
return responseObj.error;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return "An error occurred";
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// src/formatters/unknown-exception.formatter.ts
|
|
195
|
+
var UnknownExceptionFormatter = class {
|
|
196
|
+
supports(_exception) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
format(_exception) {
|
|
200
|
+
return [
|
|
201
|
+
{
|
|
202
|
+
path: DEFAULT_PATH,
|
|
203
|
+
message: "Internal server error"
|
|
204
|
+
}
|
|
205
|
+
];
|
|
206
|
+
}
|
|
207
|
+
message(_exception) {
|
|
208
|
+
return "Internal server error";
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// src/services/exception-handler.service.ts
|
|
213
|
+
import { Injectable } from "@nestjs/common";
|
|
214
|
+
var ExceptionHandlerService = class {
|
|
215
|
+
constructor() {
|
|
216
|
+
this.formatters = [];
|
|
217
|
+
this.defaultFormatter = new UnknownExceptionFormatter();
|
|
218
|
+
}
|
|
219
|
+
registerFormatter(formatter) {
|
|
220
|
+
this.formatters.push(formatter);
|
|
221
|
+
}
|
|
222
|
+
getFormatter(exception) {
|
|
223
|
+
for (const formatter of this.formatters) {
|
|
224
|
+
if (formatter.supports(exception)) {
|
|
225
|
+
return formatter;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return this.defaultFormatter;
|
|
229
|
+
}
|
|
230
|
+
formatException(exception) {
|
|
231
|
+
const formatter = this.getFormatter(exception);
|
|
232
|
+
const errors = formatter.format(exception);
|
|
233
|
+
const message = formatter.message(exception);
|
|
234
|
+
return { errors, message };
|
|
235
|
+
}
|
|
236
|
+
formatErrors(exception) {
|
|
237
|
+
const formatter = this.getFormatter(exception);
|
|
238
|
+
return formatter.format(exception);
|
|
239
|
+
}
|
|
240
|
+
getErrorMessage(exception) {
|
|
241
|
+
const formatter = this.getFormatter(exception);
|
|
242
|
+
return formatter.message(exception);
|
|
243
|
+
}
|
|
244
|
+
getAllFormatters() {
|
|
245
|
+
return [...this.formatters];
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
ExceptionHandlerService = __decorateClass([
|
|
249
|
+
Injectable()
|
|
250
|
+
], ExceptionHandlerService);
|
|
251
|
+
|
|
252
|
+
// src/filter/global-exception.filter.ts
|
|
253
|
+
import {
|
|
254
|
+
Catch,
|
|
255
|
+
HttpException as HttpException2,
|
|
256
|
+
HttpStatus,
|
|
257
|
+
Logger
|
|
258
|
+
} from "@nestjs/common";
|
|
259
|
+
var GlobalExceptionFilter = class {
|
|
260
|
+
constructor(exceptionHandlerService, config) {
|
|
261
|
+
this.exceptionHandlerService = exceptionHandlerService;
|
|
262
|
+
this.logger = new Logger(GlobalExceptionFilter.name);
|
|
263
|
+
this.config = config || { enableLogging: true, hideStackTrace: false };
|
|
264
|
+
}
|
|
265
|
+
catch(exception, host) {
|
|
266
|
+
const ctx = host.switchToHttp();
|
|
267
|
+
const response = ctx.getResponse();
|
|
268
|
+
const request = ctx.getRequest();
|
|
269
|
+
const { errors, message } = this.exceptionHandlerService.formatException(exception);
|
|
270
|
+
const status = this.getStatusCode(exception);
|
|
271
|
+
const errorResponse = {
|
|
272
|
+
success: false,
|
|
273
|
+
message,
|
|
274
|
+
errorMessages: errors
|
|
275
|
+
};
|
|
276
|
+
if (this.config.enableLogging) {
|
|
277
|
+
this.logError(request, status, errors, exception);
|
|
278
|
+
}
|
|
279
|
+
response.status(status).json(errorResponse);
|
|
280
|
+
}
|
|
281
|
+
getStatusCode(exception) {
|
|
282
|
+
if (exception instanceof HttpException2) {
|
|
283
|
+
return exception.getStatus();
|
|
284
|
+
}
|
|
285
|
+
return HttpStatus.INTERNAL_SERVER_ERROR;
|
|
286
|
+
}
|
|
287
|
+
logError(request, status, errorMessages, exception) {
|
|
288
|
+
const method = request.method;
|
|
289
|
+
const url = request.url;
|
|
290
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
291
|
+
const logData = {
|
|
292
|
+
timestamp,
|
|
293
|
+
method,
|
|
294
|
+
url,
|
|
295
|
+
status,
|
|
296
|
+
errorMessages,
|
|
297
|
+
stack: this.config.hideStackTrace ? void 0 : exception?.stack
|
|
298
|
+
};
|
|
299
|
+
this.logger.error(`${method} ${url} - ${status}`, JSON.stringify(logData));
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
GlobalExceptionFilter = __decorateClass([
|
|
303
|
+
Catch()
|
|
304
|
+
], GlobalExceptionFilter);
|
|
305
|
+
|
|
306
|
+
// src/module/exception-handler.module.ts
|
|
307
|
+
import { Module } from "@nestjs/common";
|
|
308
|
+
var ExceptionHandlerModule = class {
|
|
309
|
+
static forRoot(config) {
|
|
310
|
+
const providers = [
|
|
311
|
+
ExceptionHandlerService,
|
|
312
|
+
{
|
|
313
|
+
provide: GlobalExceptionFilter,
|
|
314
|
+
useFactory: (service) => {
|
|
315
|
+
return new GlobalExceptionFilter(service, config);
|
|
316
|
+
},
|
|
317
|
+
inject: [ExceptionHandlerService]
|
|
318
|
+
}
|
|
319
|
+
];
|
|
320
|
+
return {
|
|
321
|
+
module: ExceptionHandlerModule,
|
|
322
|
+
providers,
|
|
323
|
+
exports: [ExceptionHandlerService, GlobalExceptionFilter],
|
|
324
|
+
global: true
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
static forFeature(config) {
|
|
328
|
+
return this.forRoot(config);
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
ExceptionHandlerModule = __decorateClass([
|
|
332
|
+
Module({})
|
|
333
|
+
], ExceptionHandlerModule);
|
|
334
|
+
function initializeFormatters(service) {
|
|
335
|
+
service.registerFormatter(new PrismaExceptionFormatter());
|
|
336
|
+
service.registerFormatter(new DtoExceptionFormatter());
|
|
337
|
+
service.registerFormatter(new HttpExceptionFormatter());
|
|
338
|
+
service.registerFormatter(new UnknownExceptionFormatter());
|
|
339
|
+
}
|
|
340
|
+
export {
|
|
341
|
+
DEFAULT_ERROR_MESSAGES,
|
|
342
|
+
DEFAULT_PATH,
|
|
343
|
+
DtoExceptionFormatter,
|
|
344
|
+
ExceptionHandlerModule,
|
|
345
|
+
ExceptionHandlerService,
|
|
346
|
+
GlobalExceptionFilter,
|
|
347
|
+
HttpExceptionFormatter,
|
|
348
|
+
PRISMA_ERROR_MESSAGES,
|
|
349
|
+
PrismaExceptionFormatter,
|
|
350
|
+
UnknownExceptionFormatter,
|
|
351
|
+
initializeFormatters,
|
|
352
|
+
isPrismaError
|
|
353
|
+
};
|
|
354
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants/default-messages.ts","../src/utils/is-prisma-error.ts","../src/formatters/prisma-exception.formatter.ts","../src/formatters/dto-exception.formatter.ts","../src/formatters/http-exception.formatter.ts","../src/formatters/unknown-exception.formatter.ts","../src/services/exception-handler.service.ts","../src/filter/global-exception.filter.ts","../src/module/exception-handler.module.ts"],"sourcesContent":["export const DEFAULT_ERROR_MESSAGES = {\n UNKNOWN_ERROR: 'An unexpected error occurred',\n VALIDATION_ERROR: 'Validation failed',\n DATABASE_ERROR: 'Database error',\n NOT_FOUND: 'Resource not found',\n UNAUTHORIZED: 'Unauthorized access',\n FORBIDDEN: 'Access forbidden',\n CONFLICT: 'Conflict occurred',\n BAD_REQUEST: 'Bad request',\n};\n\nexport const PRISMA_ERROR_MESSAGES: Record<string, string> = {\n P2002: 'A record with this {field} already exists.',\n P2003: 'This {field} does not exist.',\n P2005: 'Invalid value for {field}.',\n P2006: 'Invalid format for {field}.',\n P2025: 'Record not found.',\n};\n\nexport const DEFAULT_PATH = 'unknown';\n","export function isPrismaError(exception: unknown): boolean {\n if (!exception || typeof exception !== 'object') {\n return false;\n }\n\n const error = exception as Record<string, unknown>;\n\n // Check for Prisma error characteristics\n const hasErrorCode = typeof error.code === 'string';\n const hasClientVersion = typeof error.clientVersion === 'string';\n const hasMeta = typeof error.meta === 'object';\n\n // Prisma errors typically have these properties\n return hasErrorCode && (hasClientVersion || hasMeta);\n}\n","import { ExceptionFormatter } from '../interfaces/exception-formatter.interface';\nimport { ErrorMessage } from '../interfaces/error-message.interface';\nimport { isPrismaError } from '../utils/is-prisma-error';\nimport { PRISMA_ERROR_MESSAGES } from '../constants/default-messages';\n\nexport class PrismaExceptionFormatter implements ExceptionFormatter {\n supports(exception: unknown): boolean {\n return isPrismaError(exception);\n }\n\n format(exception: unknown): ErrorMessage[] {\n const error = exception as Record<string, unknown>;\n const code = error.code as string;\n const meta = error.meta as Record<string, string> | undefined;\n const target = error.target as string[] | undefined;\n\n const messageTemplate = PRISMA_ERROR_MESSAGES[code] || 'Database operation failed';\n let path = 'unknown';\n let message = messageTemplate;\n\n if (code === 'P2002' && target && target.length > 0) {\n path = target[0];\n message = messageTemplate.replace('{field}', path);\n } else if (code === 'P2003' && meta?.field_name) {\n path = meta.field_name;\n message = messageTemplate.replace('{field}', path);\n } else if (code === 'P2005' && meta?.field_name) {\n path = meta.field_name;\n message = messageTemplate.replace('{field}', path);\n } else if (code === 'P2006' && meta?.field_name) {\n path = meta.field_name;\n message = messageTemplate.replace('{field}', path);\n } else if (code === 'P2025') {\n path = 'record';\n message = messageTemplate;\n }\n\n return [{ path, message }];\n }\n\n message(exception: unknown): string {\n const error = exception as Record<string, unknown>;\n const code = error.code as string;\n return PRISMA_ERROR_MESSAGES[code] || 'Database error';\n }\n}\n","import { ValidationError } from '@nestjs/common';\nimport { ExceptionFormatter } from '../interfaces/exception-formatter.interface';\nimport { ErrorMessage } from '../interfaces/error-message.interface';\n\nexport class DtoExceptionFormatter implements ExceptionFormatter {\n supports(exception: unknown): boolean {\n if (!exception || typeof exception !== 'object') {\n return false;\n }\n\n const error = exception as Record<string, unknown>;\n return (\n Array.isArray(error.validationErrors) ||\n (Array.isArray(error.children) && error.constraints !== undefined)\n );\n }\n\n format(exception: unknown): ErrorMessage[] {\n const error = exception as Record<string, unknown>;\n const validationErrors = error.validationErrors as ValidationError[] | undefined;\n const children = error.children as ValidationError[] | undefined;\n\n if (validationErrors) {\n return this.formatValidationErrors(validationErrors);\n }\n\n if (children) {\n return this.formatChildrenErrors(children);\n }\n\n return [{ path: 'unknown', message: 'Validation failed' }];\n }\n\n message(exception: unknown): string {\n const error = exception as Record<string, unknown>;\n const validationErrors = error.validationErrors as ValidationError[] | undefined;\n const children = error.children as ValidationError[] | undefined;\n\n if (validationErrors && validationErrors.length > 0) {\n const firstError = validationErrors[0];\n const constraints = firstError.constraints;\n if (constraints) {\n return Object.values(constraints)[0];\n }\n }\n\n if (children && children.length > 0) {\n return 'Validation failed for nested fields';\n }\n\n return 'Validation failed';\n }\n\n private formatValidationErrors(errors: ValidationError[]): ErrorMessage[] {\n return errors.flatMap((error) => {\n const constraints = error.constraints;\n if (constraints) {\n return Object.entries(constraints).map(([, value]) => ({\n path: error.property,\n message: value,\n }));\n }\n return [];\n });\n }\n\n private formatChildrenErrors(children: ValidationError[]): ErrorMessage[] {\n return children.flatMap((child) => {\n const constraints = child.constraints;\n if (constraints) {\n return Object.entries(constraints).map(([, value]) => ({\n path: child.property,\n message: value,\n }));\n }\n return [];\n });\n }\n}\n","import { HttpException } from '@nestjs/common';\nimport { ExceptionFormatter } from '../interfaces/exception-formatter.interface';\nimport { ErrorMessage } from '../interfaces/error-message.interface';\n\nexport class HttpExceptionFormatter implements ExceptionFormatter {\n supports(exception: unknown): boolean {\n return exception instanceof HttpException;\n }\n\n format(exception: unknown): ErrorMessage[] {\n const httpException = exception as HttpException;\n const response = httpException.getResponse();\n\n if (typeof response === 'string') {\n return [{ path: 'unknown', message: response }];\n }\n\n if (typeof response === 'object' && response !== null) {\n const responseObj = response as Record<string, unknown>;\n\n if (responseObj.message && Array.isArray(responseObj.message)) {\n // Handle validation errors from class-validator in HTTP exceptions\n return (responseObj.message as string[]).map((msg) => ({\n path: 'unknown',\n message: msg,\n }));\n }\n\n if (responseObj.message && typeof responseObj.message === 'string') {\n return [{ path: 'unknown', message: responseObj.message }];\n }\n\n if (responseObj.error && typeof responseObj.error === 'string') {\n return [{ path: 'unknown', message: responseObj.error }];\n }\n }\n\n return [{ path: 'unknown', message: 'An error occurred' }];\n }\n\n message(exception: unknown): string {\n const httpException = exception as HttpException;\n const response = httpException.getResponse();\n\n if (typeof response === 'string') {\n return response;\n }\n\n if (typeof response === 'object' && response !== null) {\n const responseObj = response as Record<string, unknown>;\n\n if (responseObj.message && typeof responseObj.message === 'string') {\n return responseObj.message;\n }\n\n if (responseObj.error && typeof responseObj.error === 'string') {\n return responseObj.error;\n }\n }\n\n return 'An error occurred';\n }\n}\n","import { ExceptionFormatter } from '../interfaces/exception-formatter.interface';\nimport { ErrorMessage } from '../interfaces/error-message.interface';\nimport { DEFAULT_PATH } from '../constants/default-messages';\n\nexport class UnknownExceptionFormatter implements ExceptionFormatter {\n supports(_exception: unknown): boolean {\n return true;\n }\n\n format(_exception: unknown): ErrorMessage[] {\n return [\n {\n path: DEFAULT_PATH,\n message: 'Internal server error',\n },\n ];\n }\n\n message(_exception: unknown): string {\n return 'Internal server error';\n }\n}\n","import { Injectable } from '@nestjs/common';\nimport { ExceptionFormatter } from '../interfaces/exception-formatter.interface';\nimport { ErrorMessage } from '../interfaces/error-message.interface';\nimport { UnknownExceptionFormatter } from '../formatters/unknown-exception.formatter';\n\n@Injectable()\nexport class ExceptionHandlerService {\n private formatters: ExceptionFormatter[] = [];\n private defaultFormatter: ExceptionFormatter;\n\n constructor() {\n this.defaultFormatter = new UnknownExceptionFormatter();\n }\n\n registerFormatter(formatter: ExceptionFormatter): void {\n this.formatters.push(formatter);\n }\n\n getFormatter(exception: unknown): ExceptionFormatter {\n for (const formatter of this.formatters) {\n if (formatter.supports(exception)) {\n return formatter;\n }\n }\n return this.defaultFormatter;\n }\n\n formatException(exception: unknown): { errors: ErrorMessage[]; message: string } {\n const formatter = this.getFormatter(exception);\n const errors = formatter.format(exception);\n const message = formatter.message(exception);\n\n return { errors, message };\n }\n\n formatErrors(exception: unknown): ErrorMessage[] {\n const formatter = this.getFormatter(exception);\n return formatter.format(exception);\n }\n\n getErrorMessage(exception: unknown): string {\n const formatter = this.getFormatter(exception);\n return formatter.message(exception);\n }\n\n getAllFormatters(): ExceptionFormatter[] {\n return [...this.formatters];\n }\n}\n","import {\n ExceptionFilter,\n Catch,\n ArgumentsHost,\n HttpException,\n HttpStatus,\n Logger,\n} from '@nestjs/common';\nimport { Request, Response } from 'express';\nimport { ExceptionHandlerService } from '../services/exception-handler.service';\nimport { StandardErrorResponse } from '../interfaces/error-message.interface';\nimport { ExceptionHandlerConfig } from '../interfaces/exception-handler-config.interface';\n\n@Catch()\nexport class GlobalExceptionFilter implements ExceptionFilter {\n private readonly logger = new Logger(GlobalExceptionFilter.name);\n private config: ExceptionHandlerConfig;\n\n constructor(\n private exceptionHandlerService: ExceptionHandlerService,\n config?: ExceptionHandlerConfig,\n ) {\n this.config = config || { enableLogging: true, hideStackTrace: false };\n }\n\n catch(exception: unknown, host: ArgumentsHost): void {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse<Response>();\n const request = ctx.getRequest<Request>();\n\n const { errors, message } = this.exceptionHandlerService.formatException(exception);\n const status = this.getStatusCode(exception);\n\n const errorResponse: StandardErrorResponse = {\n success: false,\n message,\n errorMessages: errors,\n };\n\n if (this.config.enableLogging) {\n this.logError(request, status, errors, exception);\n }\n\n response.status(status).json(errorResponse);\n }\n\n private getStatusCode(exception: unknown): number {\n if (exception instanceof HttpException) {\n return exception.getStatus();\n }\n return HttpStatus.INTERNAL_SERVER_ERROR;\n }\n\n private logError(\n request: Request,\n status: number,\n errorMessages: { path: string; message: string }[],\n exception: unknown,\n ): void {\n const method = request.method;\n const url = request.url;\n const timestamp = new Date().toISOString();\n\n const logData = {\n timestamp,\n method,\n url,\n status,\n errorMessages,\n stack: this.config.hideStackTrace ? undefined : (exception as Error)?.stack,\n };\n\n this.logger.error(`${method} ${url} - ${status}`, JSON.stringify(logData));\n }\n}\n","import { DynamicModule, Module } from '@nestjs/common';\nimport { ExceptionHandlerService } from '../services/exception-handler.service';\nimport { GlobalExceptionFilter } from '../filter/global-exception.filter';\nimport { ExceptionHandlerConfig } from '../interfaces/exception-handler-config.interface';\nimport { PrismaExceptionFormatter } from '../formatters/prisma-exception.formatter';\nimport { DtoExceptionFormatter } from '../formatters/dto-exception.formatter';\nimport { HttpExceptionFormatter } from '../formatters/http-exception.formatter';\nimport { UnknownExceptionFormatter } from '../formatters/unknown-exception.formatter';\n\n@Module({})\nexport class ExceptionHandlerModule {\n static forRoot(config?: ExceptionHandlerConfig): DynamicModule {\n const providers = [\n ExceptionHandlerService,\n {\n provide: GlobalExceptionFilter,\n useFactory: (service: ExceptionHandlerService) => {\n return new GlobalExceptionFilter(service, config);\n },\n inject: [ExceptionHandlerService],\n },\n ];\n\n return {\n module: ExceptionHandlerModule,\n providers,\n exports: [ExceptionHandlerService, GlobalExceptionFilter],\n global: true,\n };\n }\n\n static forFeature(config?: ExceptionHandlerConfig): DynamicModule {\n return this.forRoot(config);\n }\n}\n\n// Initialize formatters\nexport function initializeFormatters(service: ExceptionHandlerService): void {\n service.registerFormatter(new PrismaExceptionFormatter());\n service.registerFormatter(new DtoExceptionFormatter());\n service.registerFormatter(new HttpExceptionFormatter());\n service.registerFormatter(new UnknownExceptionFormatter());\n}\n"],"mappings":"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","names":["HttpException","HttpException"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nestjs-exception-handler",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A production-grade global exception handling system for NestJS applications",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"lint": "eslint src --ext .ts",
|
|
21
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
22
|
+
"test": "jest",
|
|
23
|
+
"test:watch": "jest --watch",
|
|
24
|
+
"test:cov": "jest --coverage",
|
|
25
|
+
"prepublishOnly": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"nestjs",
|
|
29
|
+
"exception",
|
|
30
|
+
"handler",
|
|
31
|
+
"filter",
|
|
32
|
+
"error",
|
|
33
|
+
"prisma",
|
|
34
|
+
"validation"
|
|
35
|
+
],
|
|
36
|
+
"author": "Nurul Islam Rimon",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/nurulislamrimon/nestjs-exception-handler.git"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@nestjs/common": "^10.0.0",
|
|
44
|
+
"@nestjs/core": "^10.0.0",
|
|
45
|
+
"@prisma/client": "^5.0.0",
|
|
46
|
+
"reflect-metadata": "^0.1.13",
|
|
47
|
+
"rxjs": "^7.8.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@nestjs/common": "^10.0.0",
|
|
51
|
+
"@nestjs/core": "^10.0.0",
|
|
52
|
+
"@nestjs/testing": "^10.4.22",
|
|
53
|
+
"@prisma/client": "^5.0.0",
|
|
54
|
+
"@types/express": "^4.17.17",
|
|
55
|
+
"@types/jest": "^29.5.0",
|
|
56
|
+
"@types/node": "^20.0.0",
|
|
57
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
58
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
59
|
+
"eslint": "^8.0.0",
|
|
60
|
+
"eslint-config-prettier": "^10.1.8",
|
|
61
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
62
|
+
"jest": "^29.5.0",
|
|
63
|
+
"prettier": "^3.0.0",
|
|
64
|
+
"reflect-metadata": "^0.1.13",
|
|
65
|
+
"rxjs": "^7.8.0",
|
|
66
|
+
"ts-jest": "^29.1.0",
|
|
67
|
+
"ts-node": "^10.9.2",
|
|
68
|
+
"tsup": "^7.0.0",
|
|
69
|
+
"typescript": "^5.0.0"
|
|
70
|
+
},
|
|
71
|
+
"dependencies": {}
|
|
72
|
+
}
|