create-charcole 1.1.0 → 2.0.1
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/.github/workflows/release.yml +26 -0
- package/CHANGELOG.md +25 -0
- package/README.md +11 -1
- package/bin/index.js +90 -71
- package/bin/lib/pkgManager.js +66 -0
- package/bin/lib/templateHandler.js +70 -0
- package/package.json +4 -1
- package/template/js/basePackage.json +28 -0
- package/template/{package-lock.json → js/package-lock.json} +1253 -1253
- package/template/{package.json → js/package.json} +28 -28
- package/template/ts/.env.example +8 -0
- package/template/ts/ARCHITECTURE_DIAGRAMS.md +283 -0
- package/template/ts/CHECKLIST.md +279 -0
- package/template/ts/COMPLETE.md +405 -0
- package/template/ts/ERROR_HANDLING.md +393 -0
- package/template/ts/IMPLEMENTATION.md +368 -0
- package/template/ts/IMPLEMENTATION_COMPLETE.md +363 -0
- package/template/ts/INDEX.md +290 -0
- package/template/ts/QUICK_REFERENCE.md +270 -0
- package/template/ts/README.md +855 -0
- package/template/ts/basePackage.json +36 -0
- package/template/ts/package-lock.json +2428 -0
- package/template/ts/package.json +32 -0
- package/template/ts/src/app.js +75 -0
- package/template/ts/src/app.ts +66 -0
- package/template/ts/src/config/constants.js +20 -0
- package/template/ts/src/config/constants.ts +27 -0
- package/template/ts/src/config/env.js +26 -0
- package/template/ts/src/config/env.ts +40 -0
- package/template/ts/src/middlewares/errorHandler.js +180 -0
- package/template/ts/src/middlewares/errorHandler.ts +209 -0
- package/template/ts/src/middlewares/requestLogger.js +33 -0
- package/template/ts/src/middlewares/requestLogger.ts +38 -0
- package/template/ts/src/middlewares/validateRequest.js +42 -0
- package/template/ts/src/middlewares/validateRequest.ts +46 -0
- package/template/ts/src/modules/health/controller.js +50 -0
- package/template/ts/src/modules/health/controller.ts +64 -0
- package/template/ts/src/routes.js +17 -0
- package/template/ts/src/routes.ts +16 -0
- package/template/ts/src/server.js +38 -0
- package/template/ts/src/server.ts +42 -0
- package/template/ts/src/types/express.d.ts +9 -0
- package/template/ts/src/utils/AppError.js +182 -0
- package/template/ts/src/utils/AppError.ts +220 -0
- package/template/ts/src/utils/logger.js +73 -0
- package/template/ts/src/utils/logger.ts +55 -0
- package/template/ts/src/utils/response.js +51 -0
- package/template/ts/src/utils/response.ts +100 -0
- package/template/ts/test-api.js +100 -0
- package/template/ts/tsconfig.json +19 -0
- /package/template/{.env.example → js/.env.example} +0 -0
- /package/template/{ARCHITECTURE_DIAGRAMS.md → js/ARCHITECTURE_DIAGRAMS.md} +0 -0
- /package/template/{CHECKLIST.md → js/CHECKLIST.md} +0 -0
- /package/template/{COMPLETE.md → js/COMPLETE.md} +0 -0
- /package/template/{ERROR_HANDLING.md → js/ERROR_HANDLING.md} +0 -0
- /package/template/{IMPLEMENTATION.md → js/IMPLEMENTATION.md} +0 -0
- /package/template/{IMPLEMENTATION_COMPLETE.md → js/IMPLEMENTATION_COMPLETE.md} +0 -0
- /package/template/{INDEX.md → js/INDEX.md} +0 -0
- /package/template/{QUICK_REFERENCE.md → js/QUICK_REFERENCE.md} +0 -0
- /package/template/{README.md → js/README.md} +0 -0
- /package/template/{src → js/src}/app.js +0 -0
- /package/template/{src → js/src}/config/constants.js +0 -0
- /package/template/{src → js/src}/config/env.js +0 -0
- /package/template/{src → js/src}/middlewares/errorHandler.js +0 -0
- /package/template/{src → js/src}/middlewares/requestLogger.js +0 -0
- /package/template/{src → js/src}/middlewares/validateRequest.js +0 -0
- /package/template/{src → js/src}/modules/health/controller.js +0 -0
- /package/template/{src → js/src}/routes.js +0 -0
- /package/template/{src → js/src}/server.js +0 -0
- /package/template/{src → js/src}/utils/AppError.js +0 -0
- /package/template/{src → js/src}/utils/logger.js +0 -0
- /package/template/{src → js/src}/utils/response.js +0 -0
- /package/template/{test-api.js → js/test-api.js} +0 -0
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "charcole",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Production-grade Node.js Express API",
|
|
5
|
-
"main": "src/server.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"start": "node src/server.js",
|
|
8
|
-
"dev": "nodemon src/server.js",
|
|
9
|
-
"lint": "echo 'Add linting here'",
|
|
10
|
-
"test": "echo 'Add tests here'"
|
|
11
|
-
},
|
|
12
|
-
"engines": {
|
|
13
|
-
"node": ">=18.0.0"
|
|
14
|
-
},
|
|
15
|
-
"keywords": [],
|
|
16
|
-
"author": "",
|
|
17
|
-
"license": "ISC",
|
|
18
|
-
"type": "module",
|
|
19
|
-
"dependencies": {
|
|
20
|
-
"cors": "^2.8.5",
|
|
21
|
-
"dotenv": "^16.3.1",
|
|
22
|
-
"express": "^4.18.2",
|
|
23
|
-
"zod": "^3.22.4"
|
|
24
|
-
},
|
|
25
|
-
"devDependencies": {
|
|
26
|
-
"nodemon": "^3.0.2"
|
|
27
|
-
}
|
|
28
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "charcole",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Production-grade Node.js Express API",
|
|
5
|
+
"main": "src/server.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node src/server.js",
|
|
8
|
+
"dev": "nodemon src/server.js",
|
|
9
|
+
"lint": "echo 'Add linting here'",
|
|
10
|
+
"test": "echo 'Add tests here'"
|
|
11
|
+
},
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=18.0.0"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [],
|
|
16
|
+
"author": "",
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"type": "module",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"cors": "^2.8.5",
|
|
21
|
+
"dotenv": "^16.3.1",
|
|
22
|
+
"express": "^4.18.2",
|
|
23
|
+
"zod": "^3.22.4"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"nodemon": "^3.0.2"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# Error Handling Architecture Diagram
|
|
2
|
+
|
|
3
|
+
## Complete Error Flow
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
7
|
+
│ HTTP REQUEST │
|
|
8
|
+
└──────────────────────────────────────┬──────────────────────────────┘
|
|
9
|
+
│
|
|
10
|
+
▼
|
|
11
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
12
|
+
│ CORS & BODY PARSING MIDDLEWARE │
|
|
13
|
+
└──────────────────────────────────────┬──────────────────────────────┘
|
|
14
|
+
│
|
|
15
|
+
▼
|
|
16
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
17
|
+
│ REQUEST LOGGER MIDDLEWARE │
|
|
18
|
+
│ (Logs: method, path, query, IP, user-agent) │
|
|
19
|
+
└──────────────────────────────────────┬──────────────────────────────┘
|
|
20
|
+
│
|
|
21
|
+
▼
|
|
22
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
23
|
+
│ VALIDATION MIDDLEWARE (optional) │
|
|
24
|
+
│ │
|
|
25
|
+
│ Validates with Zod schema → throws ValidationError if fails │
|
|
26
|
+
└──────────────────────────────────────┬──────────────────────────────┘
|
|
27
|
+
│
|
|
28
|
+
▼
|
|
29
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
30
|
+
│ ROUTE HANDLER (wrapped with asyncHandler) │
|
|
31
|
+
│ │
|
|
32
|
+
│ ┌────────────────────────────────────────────────────────────────┐ │
|
|
33
|
+
│ │ asyncHandler(async (req, res) => { │ │
|
|
34
|
+
│ │ // Your handler code │ │
|
|
35
|
+
│ │ const user = await User.findById(req.params.id); │ │
|
|
36
|
+
│ │ │ │
|
|
37
|
+
│ │ if (!user) { │ │
|
|
38
|
+
│ │ throw new NotFoundError("User", { id: req.params.id }); │ │
|
|
39
|
+
│ │ } │ │
|
|
40
|
+
│ │ │ │
|
|
41
|
+
│ │ sendSuccess(res, user); │ │
|
|
42
|
+
│ │ }) │ │
|
|
43
|
+
│ └────────────────────────────────────────────────────────────────┘ │
|
|
44
|
+
└────────┬────────────────────────────────────────────────────────────┘
|
|
45
|
+
│
|
|
46
|
+
├──→ Success? ──→ sendSuccess() ──→ [END: Response sent]
|
|
47
|
+
│
|
|
48
|
+
└──→ Error thrown ✘ ──→ [Continue below]
|
|
49
|
+
│
|
|
50
|
+
▼
|
|
51
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
52
|
+
│ GLOBAL ERROR HANDLER MIDDLEWARE │
|
|
53
|
+
│ (This is where ALL errors flow!) │
|
|
54
|
+
└────┬────────────────────────────────────────────────────────────────┘
|
|
55
|
+
│
|
|
56
|
+
▼
|
|
57
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
58
|
+
│ ERROR NORMALIZATION │
|
|
59
|
+
│ │
|
|
60
|
+
│ if (err instanceof AppError) → Use as is │
|
|
61
|
+
│ if (err instanceof ZodError) → Convert to ValidationError │
|
|
62
|
+
│ if (err instanceof TypeError) → Convert to InternalServerError │
|
|
63
|
+
│ if (err instanceof ReferenceError)→ Convert to InternalServerError │
|
|
64
|
+
│ if (err instanceof SyntaxError) → Convert to InternalServerError │
|
|
65
|
+
│ if (err instanceof RangeError) → Convert to InternalServerError │
|
|
66
|
+
│ else → Wrap in InternalServerError │
|
|
67
|
+
└────┬────────────────────────────────────────────────────────────────┘
|
|
68
|
+
│
|
|
69
|
+
▼
|
|
70
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
71
|
+
│ ERROR CLASSIFICATION │
|
|
72
|
+
│ │
|
|
73
|
+
│ ┌─ Operational Error (isOperational: true) │
|
|
74
|
+
│ │ ├─ ValidationError (422) │
|
|
75
|
+
│ │ ├─ NotFoundError (404) │
|
|
76
|
+
│ │ ├─ AuthenticationError (401) │
|
|
77
|
+
│ │ ├─ AuthorizationError (403) │
|
|
78
|
+
│ │ ├─ ConflictError (409) │
|
|
79
|
+
│ │ └─ BadRequestError (400) │
|
|
80
|
+
│ │ │
|
|
81
|
+
│ └─ Programmer Error (isOperational: false) │
|
|
82
|
+
│ └─ TypeError, ReferenceError, SyntaxError, etc. │
|
|
83
|
+
└────┬────────────────────────────────────────────────────────────────┘
|
|
84
|
+
│
|
|
85
|
+
▼
|
|
86
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
87
|
+
│ ERROR LOGGING │
|
|
88
|
+
│ │
|
|
89
|
+
│ If Operational Error: │
|
|
90
|
+
│ ├─ Log as WARN level │
|
|
91
|
+
│ ├─ Include: code, message, statusCode, method, path, IP │
|
|
92
|
+
│ └─ NO stack trace (expected error) │
|
|
93
|
+
│ │
|
|
94
|
+
│ If Programmer Error: │
|
|
95
|
+
│ ├─ Log as ERROR level │
|
|
96
|
+
│ ├─ Include: code, message, statusCode, method, path, IP, stack │
|
|
97
|
+
│ └─ YES full stack trace (for debugging) │
|
|
98
|
+
└────┬────────────────────────────────────────────────────────────────┘
|
|
99
|
+
│
|
|
100
|
+
▼
|
|
101
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
102
|
+
│ SEND ERROR RESPONSE │
|
|
103
|
+
│ │
|
|
104
|
+
│ If Production & Programmer Error: │
|
|
105
|
+
│ ├─ { │
|
|
106
|
+
│ │ success: false, │
|
|
107
|
+
│ │ message: "Internal server error", │
|
|
108
|
+
│ │ code: "INTERNAL_SERVER_ERROR", │
|
|
109
|
+
│ │ timestamp: "..." │
|
|
110
|
+
│ │ } │
|
|
111
|
+
│ │ │
|
|
112
|
+
│ └─ (Details hidden from client) │
|
|
113
|
+
│ │
|
|
114
|
+
│ If Development OR Operational Error: │
|
|
115
|
+
│ ├─ { │
|
|
116
|
+
│ │ success: false, │
|
|
117
|
+
│ │ message: "Full error message", │
|
|
118
|
+
│ │ code: "ERROR_CODE", │
|
|
119
|
+
│ │ statusCode: 422, │
|
|
120
|
+
│ │ context: { ... }, │
|
|
121
|
+
│ │ errors: [ ... ], // for validation errors │
|
|
122
|
+
│ │ timestamp: "..." │
|
|
123
|
+
│ │ } │
|
|
124
|
+
│ │ │
|
|
125
|
+
│ └─ (Full details sent) │
|
|
126
|
+
└────┬────────────────────────────────────────────────────────────────┘
|
|
127
|
+
│
|
|
128
|
+
▼
|
|
129
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
130
|
+
│ HTTP RESPONSE SENT TO CLIENT │
|
|
131
|
+
│ │
|
|
132
|
+
│ Status Code: 400, 401, 403, 404, 409, 422, 500, etc. │
|
|
133
|
+
│ Body: Consistent JSON error format │
|
|
134
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Error Class Hierarchy
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
Error (JavaScript)
|
|
141
|
+
│
|
|
142
|
+
└── AppError (Base class)
|
|
143
|
+
│
|
|
144
|
+
├── ValidationError (422 - Input validation failed)
|
|
145
|
+
├── BadRequestError (400 - Malformed request)
|
|
146
|
+
├── AuthenticationError (401 - Auth failed)
|
|
147
|
+
├── AuthorizationError (403 - Permission denied)
|
|
148
|
+
├── NotFoundError (404 - Resource not found)
|
|
149
|
+
├── ConflictError (409 - Duplicate/conflict)
|
|
150
|
+
└── InternalServerError (500 - Unexpected error)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Middleware Stack Order
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
Express App
|
|
157
|
+
│
|
|
158
|
+
├─ Trust Proxy
|
|
159
|
+
├─ CORS
|
|
160
|
+
├─ JSON Body Parser
|
|
161
|
+
├─ URL Encoded Parser
|
|
162
|
+
├─ Request Timeout Handler
|
|
163
|
+
│
|
|
164
|
+
├─ requestLogger (logs all requests)
|
|
165
|
+
│
|
|
166
|
+
├─ Routes:
|
|
167
|
+
│ │
|
|
168
|
+
│ ├─ validateRequest (optional, per route)
|
|
169
|
+
│ │
|
|
170
|
+
│ └─ Route Handler (MUST wrap with asyncHandler)
|
|
171
|
+
│
|
|
172
|
+
└─ errorHandler (MUST BE LAST)
|
|
173
|
+
└─ Catches all errors and sends JSON response
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Error Classification Decision Tree
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
Error Thrown
|
|
180
|
+
│
|
|
181
|
+
┌───────────┴───────────┐
|
|
182
|
+
│ │
|
|
183
|
+
Is AppError? Is ZodError?
|
|
184
|
+
│ │
|
|
185
|
+
YES YES
|
|
186
|
+
│ │
|
|
187
|
+
┌───────▼────────┐ ┌──────▼──────────┐
|
|
188
|
+
│ Use as is │ │ Convert to │
|
|
189
|
+
│ (already has │ │ ValidationError │
|
|
190
|
+
│ isOperational)│ │ (422) │
|
|
191
|
+
└─────────────────┘ └─────────────────┘
|
|
192
|
+
│
|
|
193
|
+
┌───────────────────────┴───────────────────────┐
|
|
194
|
+
│ │
|
|
195
|
+
Is Operational? Is Programmer Error?
|
|
196
|
+
(isOperational: true) (Syntax, Type, Ref, Range)
|
|
197
|
+
│ │
|
|
198
|
+
YES YES
|
|
199
|
+
│ │
|
|
200
|
+
┌──────────▼──────────┐ ┌──────────▼──────────┐
|
|
201
|
+
│ Send full error │ │ Convert to │
|
|
202
|
+
│ details to client │ │ InternalServerError │
|
|
203
|
+
│ with code & context │ │ (500) │
|
|
204
|
+
│ │ │ isOperational: false│
|
|
205
|
+
│ Log as WARN │ │ │
|
|
206
|
+
│ (no stack) │ │ Log as ERROR │
|
|
207
|
+
│ │ │ WITH FULL STACK │
|
|
208
|
+
└─────────────────────┘ │ │
|
|
209
|
+
│ Hide details in │
|
|
210
|
+
│ production │
|
|
211
|
+
└─────────────────────┘
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Request-to-Response Timeline
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
TIME LAYER ACTION
|
|
218
|
+
════ ═════════════════════ ════════════════════════════════════════
|
|
219
|
+
|
|
220
|
+
T0 Network Request arrives at server
|
|
221
|
+
|
|
222
|
+
T1 Express Middleware CORS, Body Parser, Request Logger
|
|
223
|
+
|
|
224
|
+
T2 Route Handler validateRequest() - validates input
|
|
225
|
+
|
|
226
|
+
T3 Route Handler Handler executes (wrapped with asyncHandler)
|
|
227
|
+
├─ Success: sendSuccess() → Response sent → T6
|
|
228
|
+
└─ Error: Thrown → T4
|
|
229
|
+
|
|
230
|
+
T4 asyncHandler Catches error, passes to next(error)
|
|
231
|
+
|
|
232
|
+
T5 Error Handler Mw Normalizes error
|
|
233
|
+
Classifies (operational vs programmer)
|
|
234
|
+
Logs error with context
|
|
235
|
+
Sanitizes response (production)
|
|
236
|
+
Sends JSON response
|
|
237
|
+
|
|
238
|
+
T6 Network Response sent to client
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Environment Behavior
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
┌──────────────────────┐ ┌──────────────────────┐
|
|
245
|
+
│ DEVELOPMENT │ │ PRODUCTION │
|
|
246
|
+
│ NODE_ENV=development│ │ NODE_ENV=production │
|
|
247
|
+
└──────────┬───────────┘ └──────────┬───────────┘
|
|
248
|
+
│ │
|
|
249
|
+
│ Programmer Error: │ Programmer Error:
|
|
250
|
+
│ ┌────────────────────────┐ │ ┌────────────────────────┐
|
|
251
|
+
│ │ { │ │ │ { │
|
|
252
|
+
│ │ success: false, │ │ │ success: false, │
|
|
253
|
+
│ │ message: "TypeError: │ │ │ message: "Internal │
|
|
254
|
+
│ │ x is undefined", │ │ │ server error", │
|
|
255
|
+
│ │ code: "INTERNAL...", │ │ │ code: "INTERNAL...", │
|
|
256
|
+
│ │ stack: "TypeError... │ │ │ timestamp: "..." │
|
|
257
|
+
│ │ at handler.js:15..." │ │ │ } │
|
|
258
|
+
│ │ } │ │ │ │
|
|
259
|
+
│ └────────────────────────┘ │ └────────────────────────┘
|
|
260
|
+
│ │
|
|
261
|
+
│ Operational Error: │ Operational Error:
|
|
262
|
+
│ (sent fully in both) │ (sent fully in both)
|
|
263
|
+
│ ┌────────────────────────┐ │ ┌────────────────────────┐
|
|
264
|
+
│ │ { │ │ │ { │
|
|
265
|
+
│ │ success: false, │ │ │ success: false, │
|
|
266
|
+
│ │ message: "User not │ │ │ message: "User not │
|
|
267
|
+
│ │ found", │ │ │ found", │
|
|
268
|
+
│ │ code: "NOT_FOUND", │ │ │ code: "NOT_FOUND", │
|
|
269
|
+
│ │ context: {...} │ │ │ context: {...} │
|
|
270
|
+
│ │ } │ │ │ } │
|
|
271
|
+
│ └────────────────────────┘ │ └────────────────────────┘
|
|
272
|
+
│ │
|
|
273
|
+
│ Logging: │ Logging:
|
|
274
|
+
│ Full details + stack │ Full details + stack
|
|
275
|
+
│ (for development) │ (server-side only)
|
|
276
|
+
│ │
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
This architecture ensures that **every single error in your application flows through one place, gets properly classified, logged with full context, and returns a consistent JSON response to the client.**
|
|
282
|
+
|
|
283
|
+
That's production-grade error handling. 🎯
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# Implementation Checklist - Production Error Handling System
|
|
2
|
+
|
|
3
|
+
## ✅ Files Created
|
|
4
|
+
|
|
5
|
+
- [x] `src/utils/AppError.js` - Error class hierarchy (8 error types)
|
|
6
|
+
- [x] `ERROR_HANDLING.md` - Comprehensive documentation
|
|
7
|
+
- [x] `QUICK_REFERENCE.md` - Quick start guide
|
|
8
|
+
- [x] `ARCHITECTURE_DIAGRAMS.md` - Visual architecture diagrams
|
|
9
|
+
- [x] `IMPLEMENTATION_COMPLETE.md` - Implementation details & examples
|
|
10
|
+
- [x] `COMPLETE.md` - Final summary document
|
|
11
|
+
- [x] `test-api.js` - API testing script
|
|
12
|
+
|
|
13
|
+
## ✅ Files Updated
|
|
14
|
+
|
|
15
|
+
### Core System
|
|
16
|
+
|
|
17
|
+
- [x] `src/utils/logger.js` - Added stack trace support + fatal() method
|
|
18
|
+
- [x] `src/utils/response.js` - Added documentation, kept for backward compatibility
|
|
19
|
+
- [x] `src/middlewares/errorHandler.js` - **Complete rewrite**
|
|
20
|
+
- Global error handler with normalization
|
|
21
|
+
- asyncHandler wrapper for async routes
|
|
22
|
+
- Error classification (operational vs programmer)
|
|
23
|
+
- Intelligent logging based on error type
|
|
24
|
+
- Production-safe responses
|
|
25
|
+
- [x] `src/middlewares/validateRequest.js` - Updated to throw ValidationError
|
|
26
|
+
- [x] `src/middlewares/requestLogger.js` - Enhanced with error detection
|
|
27
|
+
- [x] `src/app.js` - Updated to use new error system
|
|
28
|
+
- [x] `src/server.js` - Enhanced with graceful shutdown
|
|
29
|
+
- [x] `src/routes.js` - Updated with example routes
|
|
30
|
+
- [x] `src/modules/health/controller.js` - Updated with asyncHandler & new error classes
|
|
31
|
+
- [x] `package.json` - Already had Zod, no changes needed
|
|
32
|
+
- [x] `.env` - Already configured
|
|
33
|
+
- [x] `.env.example` - Already configured
|
|
34
|
+
- [x] `README.md` - Already configured
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 🎯 Core Components
|
|
39
|
+
|
|
40
|
+
### 1. AppError Class Hierarchy
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
AppError (base) - isOperational, code, context, cause, timestamp
|
|
44
|
+
├── ValidationError (422)
|
|
45
|
+
├── BadRequestError (400)
|
|
46
|
+
├── AuthenticationError (401)
|
|
47
|
+
├── AuthorizationError (403)
|
|
48
|
+
├── NotFoundError (404)
|
|
49
|
+
├── ConflictError (409)
|
|
50
|
+
└── InternalServerError (500)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Methods:**
|
|
54
|
+
|
|
55
|
+
- `toJSON()` - Convert to response format
|
|
56
|
+
- `getFullDetails()` - Get full error info for logging
|
|
57
|
+
|
|
58
|
+
### 2. Global Error Handler
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
errorHandler(err, req, res, next)
|
|
62
|
+
├── normalizeError() - Convert any error type to AppError
|
|
63
|
+
├── logError() - Log with appropriate level + context
|
|
64
|
+
└── sendErrorResponse() - Send client response
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. Async Error Wrapper
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
asyncHandler(fn) - Wraps async handlers to catch errors
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Usage:**
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
router.get(
|
|
77
|
+
"/endpoint",
|
|
78
|
+
asyncHandler(async (req, res) => {
|
|
79
|
+
// Error thrown here is caught and passed to global handler
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 4. Validation Middleware
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
validateRequest(schema) - Validates body, query, params
|
|
88
|
+
├── Throws ValidationError if fails
|
|
89
|
+
└── Attaches req.validatedData if succeeds
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 5. Enhanced Logger
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
logger.debug(msg, data)
|
|
96
|
+
logger.info(msg, data)
|
|
97
|
+
logger.warn(msg, data)
|
|
98
|
+
logger.error(msg, data, stack)
|
|
99
|
+
logger.fatal(msg, data, stack)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Features:**
|
|
103
|
+
|
|
104
|
+
- Color-coded output
|
|
105
|
+
- Configurable levels
|
|
106
|
+
- Stack trace support
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 🔄 Error Flow Summary
|
|
111
|
+
|
|
112
|
+
1. **Request arrives**
|
|
113
|
+
2. **Middleware chain** (CORS, body parser, request logger)
|
|
114
|
+
3. **Validation middleware** (optional, throws ValidationError)
|
|
115
|
+
4. **Route handler** (wrapped with asyncHandler)
|
|
116
|
+
- Success → `sendSuccess(res, data)` → Response sent
|
|
117
|
+
- Error → `throw new ErrorType(...)` → Step 5
|
|
118
|
+
5. **asyncHandler catches** error → passes to next(error)
|
|
119
|
+
6. **Global error handler** catches error
|
|
120
|
+
- Normalizes: ZodError → ValidationError, TypeError → InternalServerError, etc.
|
|
121
|
+
- Classifies: operational vs programmer
|
|
122
|
+
- Logs: WARN for operational, ERROR with stack for programmer
|
|
123
|
+
- Sanitizes: hides details in production
|
|
124
|
+
- Sends: consistent JSON response
|
|
125
|
+
7. **Client receives** structured error response
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 📊 Error Classification
|
|
130
|
+
|
|
131
|
+
### Operational Errors (isOperational: true)
|
|
132
|
+
|
|
133
|
+
**Expected errors that can be handled gracefully**
|
|
134
|
+
|
|
135
|
+
- ValidationError (422) - Input validation failed
|
|
136
|
+
- BadRequestError (400) - Malformed request
|
|
137
|
+
- AuthenticationError (401) - Invalid credentials
|
|
138
|
+
- AuthorizationError (403) - Permission denied
|
|
139
|
+
- NotFoundError (404) - Resource doesn't exist
|
|
140
|
+
- ConflictError (409) - Duplicate/conflict
|
|
141
|
+
|
|
142
|
+
**Behavior:**
|
|
143
|
+
|
|
144
|
+
- ✅ Logged as WARN
|
|
145
|
+
- ✅ Full details sent to client
|
|
146
|
+
- ✅ NO stack trace logged
|
|
147
|
+
- ✅ Code included for client handling
|
|
148
|
+
|
|
149
|
+
### Programmer Errors (isOperational: false)
|
|
150
|
+
|
|
151
|
+
**Unexpected errors that indicate bugs**
|
|
152
|
+
|
|
153
|
+
- TypeError
|
|
154
|
+
- ReferenceError
|
|
155
|
+
- SyntaxError
|
|
156
|
+
- RangeError
|
|
157
|
+
- Unhandled exceptions
|
|
158
|
+
- Any error not explicitly thrown as AppError
|
|
159
|
+
|
|
160
|
+
**Behavior:**
|
|
161
|
+
|
|
162
|
+
- ✅ Logged as ERROR
|
|
163
|
+
- ✅ FULL stack trace logged
|
|
164
|
+
- ✅ Generic message sent to client in production
|
|
165
|
+
- ✅ Full details shown in development
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 🎓 Golden Rules
|
|
170
|
+
|
|
171
|
+
1. ✅ **Always wrap async handlers** with asyncHandler
|
|
172
|
+
2. ✅ **Always throw AppError** (not res.status().json())
|
|
173
|
+
3. ✅ **Always validate** with validateRequest middleware
|
|
174
|
+
4. ✅ **Always include context** when throwing errors
|
|
175
|
+
5. ✅ **Always use sendSuccess()** for success responses
|
|
176
|
+
6. ❌ **Never use res.status().json()** for errors
|
|
177
|
+
7. ❌ **Never catch errors silently**
|
|
178
|
+
8. ❌ **Never mix error handling styles**
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 🔍 Testing the System
|
|
183
|
+
|
|
184
|
+
### Manual Test Cases
|
|
185
|
+
|
|
186
|
+
1. **Valid request** → 200 with data
|
|
187
|
+
2. **Invalid input** → 422 with field errors
|
|
188
|
+
3. **Not found** → 404 with context
|
|
189
|
+
4. **Duplicate** → 409 with message
|
|
190
|
+
5. **Unauthorized** → 401 with message
|
|
191
|
+
6. **Forbidden** → 403 with message
|
|
192
|
+
7. **Programmer error** → 500 (generic in prod, detailed in dev)
|
|
193
|
+
8. **Unhandled error** → Caught and logged
|
|
194
|
+
|
|
195
|
+
### Run API Tests
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
node test-api.js
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 📈 Production Readiness
|
|
204
|
+
|
|
205
|
+
### Before Deploying
|
|
206
|
+
|
|
207
|
+
- [ ] Set `NODE_ENV=production` in .env
|
|
208
|
+
- [ ] Configure `CORS_ORIGIN` for your domain
|
|
209
|
+
- [ ] Set `LOG_LEVEL` appropriately
|
|
210
|
+
- [ ] Test all endpoints with error cases
|
|
211
|
+
- [ ] Verify error responses don't leak secrets
|
|
212
|
+
- [ ] Set up error monitoring (e.g., Sentry)
|
|
213
|
+
- [ ] Configure structured logging sink
|
|
214
|
+
- [ ] Test graceful shutdown behavior
|
|
215
|
+
|
|
216
|
+
### Error Monitoring
|
|
217
|
+
|
|
218
|
+
Monitor these metrics:
|
|
219
|
+
|
|
220
|
+
- 4xx error rate (client errors)
|
|
221
|
+
- 5xx error rate (server errors)
|
|
222
|
+
- Error rate by endpoint
|
|
223
|
+
- Error rate by error code
|
|
224
|
+
- Response time percentiles
|
|
225
|
+
- Unhandled exception rate
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 📚 Documentation Files
|
|
230
|
+
|
|
231
|
+
| File | Purpose | Audience |
|
|
232
|
+
| -------------------------------------------------------- | -------------- | ---------- |
|
|
233
|
+
| [COMPLETE.md](COMPLETE.md) | Final summary | Everyone |
|
|
234
|
+
| [QUICK_REFERENCE.md](QUICK_REFERENCE.md) | Quick start | Developers |
|
|
235
|
+
| [ERROR_HANDLING.md](ERROR_HANDLING.md) | Full guide | Architects |
|
|
236
|
+
| [ARCHITECTURE_DIAGRAMS.md](ARCHITECTURE_DIAGRAMS.md) | Visual arch | Architects |
|
|
237
|
+
| [IMPLEMENTATION_COMPLETE.md](IMPLEMENTATION_COMPLETE.md) | Implementation | DevOps |
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## 🚀 Starting the Server
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
# Development
|
|
245
|
+
npm run dev
|
|
246
|
+
|
|
247
|
+
# Production
|
|
248
|
+
npm start
|
|
249
|
+
|
|
250
|
+
# Testing
|
|
251
|
+
node test-api.js
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## ✨ What You Get
|
|
257
|
+
|
|
258
|
+
✅ Centralized error handling
|
|
259
|
+
✅ Proper error classification
|
|
260
|
+
✅ Comprehensive logging
|
|
261
|
+
✅ Secure error responses
|
|
262
|
+
✅ Development-friendly output
|
|
263
|
+
✅ Production-safe responses
|
|
264
|
+
✅ Consistent JSON format
|
|
265
|
+
✅ Full stack traces (in dev)
|
|
266
|
+
✅ Error context tracking
|
|
267
|
+
✅ Request logging
|
|
268
|
+
✅ Async error catching
|
|
269
|
+
✅ Graceful shutdown
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## 🎯 Status: COMPLETE ✅
|
|
274
|
+
|
|
275
|
+
All components implemented. All files created. All documentation written.
|
|
276
|
+
|
|
277
|
+
**Your production-grade error handling system is ready to deploy.** 🚀
|
|
278
|
+
|
|
279
|
+
Every error flows through one place. Every response is consistent. This is where engineering starts. 🎓
|