create-charcole 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/LICENSE +21 -0
- package/README.md +398 -0
- package/bin/index.js +62 -0
- package/package.json +24 -0
- package/template/.env.example +8 -0
- package/template/ARCHITECTURE_DIAGRAMS.md +283 -0
- package/template/CHECKLIST.md +279 -0
- package/template/COMPLETE.md +405 -0
- package/template/ERROR_HANDLING.md +393 -0
- package/template/IMPLEMENTATION.md +368 -0
- package/template/IMPLEMENTATION_COMPLETE.md +363 -0
- package/template/INDEX.md +290 -0
- package/template/QUICK_REFERENCE.md +270 -0
- package/template/README.md +855 -0
- package/template/package-lock.json +1253 -0
- package/template/package.json +28 -0
- package/template/src/app.js +75 -0
- package/template/src/config/constants.js +20 -0
- package/template/src/config/env.js +26 -0
- package/template/src/middlewares/errorHandler.js +180 -0
- package/template/src/middlewares/requestLogger.js +33 -0
- package/template/src/middlewares/validateRequest.js +42 -0
- package/template/src/modules/health/controller.js +50 -0
- package/template/src/routes.js +17 -0
- package/template/src/server.js +38 -0
- package/template/src/utils/AppError.js +182 -0
- package/template/src/utils/logger.js +73 -0
- package/template/src/utils/response.js +51 -0
- package/template/test-api.js +100 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/\*\*
|
|
4
|
+
|
|
5
|
+
- ╔═════════════════════════════════════════════════════════════════════════╗
|
|
6
|
+
- ║ ║
|
|
7
|
+
- ║ ✅ PRODUCTION-LEVEL ERROR HANDLING - IMPLEMENTATION SUMMARY ║
|
|
8
|
+
- ║ ║
|
|
9
|
+
- ╚═════════════════════════════════════════════════════════════════════════╝
|
|
10
|
+
-
|
|
11
|
+
- PROBLEM SOLVED:
|
|
12
|
+
- ❌ No more random res.status(500).json(...) scattered throughout code
|
|
13
|
+
- ❌ No distinction between operational and programmer errors
|
|
14
|
+
- ❌ Inconsistent error response formats
|
|
15
|
+
- ❌ No centralized error handling
|
|
16
|
+
-
|
|
17
|
+
- SOLUTION PROVIDED:
|
|
18
|
+
- ✅ Centralized error handler - ALL errors flow through one place
|
|
19
|
+
- ✅ Error classification - Operational vs Programmer errors
|
|
20
|
+
- ✅ Consistent responses - Standardized JSON format everywhere
|
|
21
|
+
- ✅ Comprehensive logging - Full context for debugging
|
|
22
|
+
- ✅ Production-safe - Hides internal details in production
|
|
23
|
+
-
|
|
24
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
25
|
+
-
|
|
26
|
+
- ERROR CLASS HIERARCHY (Use these, never res.status().json()!)
|
|
27
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
28
|
+
-
|
|
29
|
+
- AppError (base - isOperational, code, context, cause, timestamp)
|
|
30
|
+
- │
|
|
31
|
+
- ├─ ValidationError (422) → Input validation failed
|
|
32
|
+
- ├─ BadRequestError (400) → Malformed request
|
|
33
|
+
- ├─ AuthenticationError (401) → Auth credentials invalid
|
|
34
|
+
- ├─ AuthorizationError (403) → Permission denied
|
|
35
|
+
- ├─ NotFoundError (404) → Resource doesn't exist
|
|
36
|
+
- ├─ ConflictError (409) → Duplicate/conflict
|
|
37
|
+
- └─ InternalServerError (500) → Unexpected error (programmer bug)
|
|
38
|
+
-
|
|
39
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
40
|
+
-
|
|
41
|
+
- ERROR FLOW (How every error is handled)
|
|
42
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
43
|
+
-
|
|
44
|
+
- Request arrives
|
|
45
|
+
- ↓
|
|
46
|
+
- Route handler (MUST wrap with asyncHandler)
|
|
47
|
+
- ├─ Success? → sendSuccess(res, data) → Response sent ✓
|
|
48
|
+
- │
|
|
49
|
+
- └─ Error thrown ✘
|
|
50
|
+
- ↓
|
|
51
|
+
- asyncHandler catches it
|
|
52
|
+
- ↓
|
|
53
|
+
- Global error handler middleware (MUST be last)
|
|
54
|
+
- ↓
|
|
55
|
+
- Error normalized
|
|
56
|
+
- ├─ AppError? → Use as is
|
|
57
|
+
- ├─ ZodError? → Convert to ValidationError
|
|
58
|
+
- ├─ TypeError? → Convert to InternalServerError
|
|
59
|
+
- ├─ ReferenceError? → Convert to InternalServerError
|
|
60
|
+
- └─ Unknown? → Wrap in InternalServerError
|
|
61
|
+
- ↓
|
|
62
|
+
- Error classified
|
|
63
|
+
- ├─ Operational (isOperational: true)
|
|
64
|
+
- │ └─ Log as WARN, send full details to client
|
|
65
|
+
- │
|
|
66
|
+
- └─ Programmer (isOperational: false)
|
|
67
|
+
- ├─ Log as ERROR with full stack trace
|
|
68
|
+
- └─ Send generic message in production
|
|
69
|
+
- ↓
|
|
70
|
+
- Consistent JSON response sent
|
|
71
|
+
-
|
|
72
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
73
|
+
-
|
|
74
|
+
- RESPONSE FORMAT (Always consistent)
|
|
75
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
76
|
+
-
|
|
77
|
+
- SUCCESS (200, 201, etc.)
|
|
78
|
+
- {
|
|
79
|
+
- "success": true,
|
|
80
|
+
- "message": "User created successfully",
|
|
81
|
+
- "data": { ... },
|
|
82
|
+
- "timestamp": "2024-01-19T15:55:30.000Z"
|
|
83
|
+
- }
|
|
84
|
+
-
|
|
85
|
+
- OPERATIONAL ERROR (400, 401, 403, 404, 409, 422)
|
|
86
|
+
- {
|
|
87
|
+
- "success": false,
|
|
88
|
+
- "message": "User not found",
|
|
89
|
+
- "code": "NOT_FOUND",
|
|
90
|
+
- "statusCode": 404,
|
|
91
|
+
- "context": { "id": "999" },
|
|
92
|
+
- "timestamp": "2024-01-19T15:55:30.000Z"
|
|
93
|
+
- }
|
|
94
|
+
-
|
|
95
|
+
- VALIDATION ERROR (422)
|
|
96
|
+
- {
|
|
97
|
+
- "success": false,
|
|
98
|
+
- "message": "Validation failed",
|
|
99
|
+
- "code": "VALIDATION_ERROR",
|
|
100
|
+
- "statusCode": 422,
|
|
101
|
+
- "errors": [
|
|
102
|
+
- { "field": "email", "message": "Invalid email", "code": "invalid_email" }
|
|
103
|
+
- ],
|
|
104
|
+
- "timestamp": "2024-01-19T15:55:30.000Z"
|
|
105
|
+
- }
|
|
106
|
+
-
|
|
107
|
+
- PROGRAMMER ERROR (500) - PRODUCTION
|
|
108
|
+
- {
|
|
109
|
+
- "success": false,
|
|
110
|
+
- "message": "Internal server error",
|
|
111
|
+
- "code": "INTERNAL_SERVER_ERROR",
|
|
112
|
+
- "timestamp": "2024-01-19T15:55:30.000Z"
|
|
113
|
+
- }
|
|
114
|
+
- (Details hidden in production)
|
|
115
|
+
-
|
|
116
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
117
|
+
-
|
|
118
|
+
- HOW TO USE (4 Golden Rules)
|
|
119
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
120
|
+
-
|
|
121
|
+
- 1. WRAP ASYNC HANDLERS WITH asyncHandler
|
|
122
|
+
- ✅ router.post("/users", asyncHandler(async (req, res) => {
|
|
123
|
+
- const user = await createUser(...);
|
|
124
|
+
- sendSuccess(res, user, 201, "Created");
|
|
125
|
+
- }));
|
|
126
|
+
- ❌ router.post("/users", async (req, res) => { ... }); // Error leaks!
|
|
127
|
+
-
|
|
128
|
+
- 2. THROW AppError (Never use res.status().json()!)
|
|
129
|
+
- ✅ if (!user) throw new NotFoundError("User", { id });
|
|
130
|
+
- ✅ if (exists) throw new ConflictError("Email already exists");
|
|
131
|
+
- ❌ res.status(404).json({ error: "User not found" }); // WRONG!
|
|
132
|
+
-
|
|
133
|
+
- 3. VALIDATE WITH validateRequest MIDDLEWARE
|
|
134
|
+
- ✅ router.post("/users", validateRequest(schema), handler);
|
|
135
|
+
- ❌ Just accept raw data without validation
|
|
136
|
+
-
|
|
137
|
+
- 4. SEND SUCCESS WITH sendSuccess HELPER
|
|
138
|
+
- ✅ sendSuccess(res, data, 200, "Success message");
|
|
139
|
+
- ❌ res.json(data); // Inconsistent format
|
|
140
|
+
-
|
|
141
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
142
|
+
-
|
|
143
|
+
- LOGGING BEHAVIOR
|
|
144
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
145
|
+
-
|
|
146
|
+
- OPERATIONAL ERROR (WARN level - expected, can be handled)
|
|
147
|
+
- [2024-01-19T15:55:30.000Z] WARN: Operational Error: NOT_FOUND
|
|
148
|
+
- {
|
|
149
|
+
- "type": "OPERATIONAL",
|
|
150
|
+
- "code": "NOT_FOUND",
|
|
151
|
+
- "message": "User not found",
|
|
152
|
+
- "statusCode": 404,
|
|
153
|
+
- "method": "GET",
|
|
154
|
+
- "path": "/api/users/999"
|
|
155
|
+
- }
|
|
156
|
+
-
|
|
157
|
+
- PROGRAMMER ERROR (ERROR level - unexpected, needs fixing)
|
|
158
|
+
- [2024-01-19T15:55:30.000Z] ERROR: Programmer Error: REFERENCE_ERROR
|
|
159
|
+
- {
|
|
160
|
+
- "type": "PROGRAMMER",
|
|
161
|
+
- "code": "REFERENCE_ERROR",
|
|
162
|
+
- "message": "user is not defined",
|
|
163
|
+
- "statusCode": 500,
|
|
164
|
+
- "method": "GET",
|
|
165
|
+
- "path": "/api/users/123"
|
|
166
|
+
- }
|
|
167
|
+
- ReferenceError: user is not defined
|
|
168
|
+
- at getUserHandler (/app/src/modules/users/controller.js:15:3)
|
|
169
|
+
- at processRequest (/app/src/middlewares/errorHandler.js:42:5)
|
|
170
|
+
- ...
|
|
171
|
+
-
|
|
172
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
173
|
+
-
|
|
174
|
+
- FILES CREATED
|
|
175
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
176
|
+
-
|
|
177
|
+
- NEW CORE FILES:
|
|
178
|
+
- • src/utils/AppError.js (8 specialized error classes)
|
|
179
|
+
-
|
|
180
|
+
- DOCUMENTATION:
|
|
181
|
+
- • INDEX.md (Start here!)
|
|
182
|
+
- • QUICK_REFERENCE.md (Quick patterns)
|
|
183
|
+
- • ERROR_HANDLING.md (Comprehensive guide)
|
|
184
|
+
- • ARCHITECTURE_DIAGRAMS.md (Visual architecture)
|
|
185
|
+
- • IMPLEMENTATION_COMPLETE.md (Full details)
|
|
186
|
+
- • COMPLETE.md (Final summary)
|
|
187
|
+
- • CHECKLIST.md (Status checklist)
|
|
188
|
+
-
|
|
189
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
190
|
+
-
|
|
191
|
+
- FILES UPDATED
|
|
192
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
193
|
+
-
|
|
194
|
+
- CORE SYSTEM:
|
|
195
|
+
- • src/middlewares/errorHandler.js (REWRITTEN - Global error handler)
|
|
196
|
+
- • src/middlewares/validateRequest.js (Updated to throw ValidationError)
|
|
197
|
+
- • src/middlewares/requestLogger.js (Enhanced logging)
|
|
198
|
+
- • src/utils/logger.js (Added stack trace support)
|
|
199
|
+
- • src/utils/response.js (Added documentation)
|
|
200
|
+
- • src/app.js (Integrated error handler)
|
|
201
|
+
- • src/server.js (Enhanced shutdown)
|
|
202
|
+
- • src/routes.js (Updated routes)
|
|
203
|
+
- • src/modules/health/controller.js (Updated handlers)
|
|
204
|
+
-
|
|
205
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
206
|
+
-
|
|
207
|
+
- RUNNING THE SERVER
|
|
208
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
209
|
+
-
|
|
210
|
+
- DEVELOPMENT (with auto-reload):
|
|
211
|
+
- $ npm run dev
|
|
212
|
+
-
|
|
213
|
+
- PRODUCTION:
|
|
214
|
+
- $ npm start
|
|
215
|
+
-
|
|
216
|
+
- SERVER STARTS WITH:
|
|
217
|
+
- [2024-01-19T15:55:01.329Z] INFO: Express app configured successfully
|
|
218
|
+
- [2024-01-19T15:55:01.329Z] INFO: 🔥 Server running in development mode {
|
|
219
|
+
- "url": "http://localhost:3000",
|
|
220
|
+
- "port": 3000
|
|
221
|
+
- }
|
|
222
|
+
-
|
|
223
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
224
|
+
-
|
|
225
|
+
- EXAMPLE: CREATE USER ENDPOINT
|
|
226
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
227
|
+
-
|
|
228
|
+
- import { asyncHandler, ConflictError } from "./middlewares/errorHandler.js";
|
|
229
|
+
- import { sendSuccess } from "./utils/response.js";
|
|
230
|
+
- import { validateRequest } from "./middlewares/validateRequest.js";
|
|
231
|
+
- import { z } from "zod";
|
|
232
|
+
-
|
|
233
|
+
- // 1. Define validation schema
|
|
234
|
+
- const createUserSchema = z.object({
|
|
235
|
+
- body: z.object({
|
|
236
|
+
- email: z.string().email("Invalid email"),
|
|
237
|
+
- name: z.string().min(1, "Name required"),
|
|
238
|
+
- }),
|
|
239
|
+
- });
|
|
240
|
+
-
|
|
241
|
+
- // 2. Define handler (wrapped with asyncHandler)
|
|
242
|
+
- export const createUser = asyncHandler(async (req, res) => {
|
|
243
|
+
- const { email, name } = req.validatedData.body;
|
|
244
|
+
-
|
|
245
|
+
- // Check for duplicate (throw operational error)
|
|
246
|
+
- const exists = await User.findOne({ email });
|
|
247
|
+
- if (exists) {
|
|
248
|
+
- throw new ConflictError("Email already exists", { email });
|
|
249
|
+
- }
|
|
250
|
+
-
|
|
251
|
+
- // Create user (any error thrown here is caught by global handler)
|
|
252
|
+
- const user = await User.create({ email, name });
|
|
253
|
+
-
|
|
254
|
+
- // Send success response
|
|
255
|
+
- sendSuccess(res, user, 201, "User created successfully");
|
|
256
|
+
- });
|
|
257
|
+
-
|
|
258
|
+
- // 3. Use in routes
|
|
259
|
+
- router.post("/users", validateRequest(createUserSchema), createUser);
|
|
260
|
+
-
|
|
261
|
+
- RESULT:
|
|
262
|
+
- ✅ Validation error (422) → Field-level details
|
|
263
|
+
- ✅ Duplicate email (409) → Conflict error
|
|
264
|
+
- ✅ Database error (500) → Stack trace logged, generic message sent
|
|
265
|
+
- ✅ Success (201) → Consistent success response
|
|
266
|
+
-
|
|
267
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
268
|
+
-
|
|
269
|
+
- KEY FEATURES
|
|
270
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
271
|
+
-
|
|
272
|
+
- ✅ Centralized error handling - All errors flow through one place
|
|
273
|
+
- ✅ Error classification - Operational vs Programmer errors
|
|
274
|
+
- ✅ 8 specialized error classes - For every common scenario
|
|
275
|
+
- ✅ Async error wrapper - Prevents promise rejection leaks
|
|
276
|
+
- ✅ Validation formatting - Field-level error details
|
|
277
|
+
- ✅ Comprehensive logging - Full context for debugging
|
|
278
|
+
- ✅ Stack trace logging - For programmer error investigation
|
|
279
|
+
- ✅ Production-safe responses - Hides internal details in prod
|
|
280
|
+
- ✅ Request logging - Method, path, status, duration
|
|
281
|
+
- ✅ Graceful shutdown - Proper cleanup on signals
|
|
282
|
+
- ✅ Unhandled exception catching - Catches all edge cases
|
|
283
|
+
- ✅ Consistent JSON format - All responses standardized
|
|
284
|
+
-
|
|
285
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
286
|
+
-
|
|
287
|
+
- DOCUMENTATION
|
|
288
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
289
|
+
-
|
|
290
|
+
- START HERE:
|
|
291
|
+
- INDEX.md - Navigation and overview
|
|
292
|
+
-
|
|
293
|
+
- QUICK START:
|
|
294
|
+
- QUICK_REFERENCE.md - Patterns and golden rules
|
|
295
|
+
-
|
|
296
|
+
- COMPREHENSIVE GUIDE:
|
|
297
|
+
- ERROR_HANDLING.md - Full documentation with examples
|
|
298
|
+
-
|
|
299
|
+
- ARCHITECTURE:
|
|
300
|
+
- ARCHITECTURE_DIAGRAMS.md - Visual flow and diagrams
|
|
301
|
+
-
|
|
302
|
+
- DETAILS:
|
|
303
|
+
- IMPLEMENTATION_COMPLETE.md - Full implementation details
|
|
304
|
+
- COMPLETE.md - Final summary
|
|
305
|
+
- CHECKLIST.md - Status and checklist
|
|
306
|
+
-
|
|
307
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
308
|
+
-
|
|
309
|
+
- STATUS: ✅ PRODUCTION-READY
|
|
310
|
+
-
|
|
311
|
+
- Your API now has enterprise-grade error handling.
|
|
312
|
+
- Every error flows through one place.
|
|
313
|
+
- Every response is consistent.
|
|
314
|
+
- This is where engineering starts.
|
|
315
|
+
-
|
|
316
|
+
- ═════════════════════════════════════════════════════════════════════════
|
|
317
|
+
\*/
|
|
318
|
+
|
|
319
|
+
console.log(`╔═════════════════════════════════════════════════════════════════════════╗
|
|
320
|
+
║ ║
|
|
321
|
+
║ ✅ PRODUCTION-LEVEL ERROR HANDLING - IMPLEMENTATION COMPLETE ║
|
|
322
|
+
║ ║
|
|
323
|
+
║ 🎯 SOLVED: ║
|
|
324
|
+
║ No more res.status(500).json(...) scattered everywhere! ║
|
|
325
|
+
║ ║
|
|
326
|
+
║ ✨ FEATURES: ║
|
|
327
|
+
║ ✅ Centralized error handler ║
|
|
328
|
+
║ ✅ Error classification (operational vs programmer) ║
|
|
329
|
+
║ ✅ 8 specialized error classes ║
|
|
330
|
+
║ ✅ Async error wrapper ║
|
|
331
|
+
║ ✅ Validation error formatting ║
|
|
332
|
+
║ ✅ Comprehensive logging with stacks ║
|
|
333
|
+
║ ✅ Production-safe responses ║
|
|
334
|
+
║ ✅ Consistent JSON format ║
|
|
335
|
+
║ ✅ Request logging ║
|
|
336
|
+
║ ✅ Graceful shutdown ║
|
|
337
|
+
║ ║
|
|
338
|
+
║ 🚀 QUICK START: ║
|
|
339
|
+
║ ║
|
|
340
|
+
║ 1. Wrap async handlers: ║
|
|
341
|
+
║ router.get("/users/:id", asyncHandler(async (req, res) => {})) ║
|
|
342
|
+
║ ║
|
|
343
|
+
║ 2. Throw AppError: ║
|
|
344
|
+
║ throw new NotFoundError("User", { id }); ║
|
|
345
|
+
║ ║
|
|
346
|
+
║ 3. Validate requests: ║
|
|
347
|
+
║ router.post("/users", validateRequest(schema), handler) ║
|
|
348
|
+
║ ║
|
|
349
|
+
║ 4. Send success: ║
|
|
350
|
+
║ sendSuccess(res, data, 201, "Created"); ║
|
|
351
|
+
║ ║
|
|
352
|
+
║ 📚 DOCUMENTATION: ║
|
|
353
|
+
║ • INDEX.md - Start here! ║
|
|
354
|
+
║ • QUICK_REFERENCE.md - Patterns & rules ║
|
|
355
|
+
║ • ERROR_HANDLING.md - Comprehensive guide ║
|
|
356
|
+
║ • ARCHITECTURE_DIAGRAMS.md - Visual diagrams ║
|
|
357
|
+
║ ║
|
|
358
|
+
║ 🚀 RUNNING: ║
|
|
359
|
+
║ npm run dev (development with auto-reload) ║
|
|
360
|
+
║ npm start (production) ║
|
|
361
|
+
║ ║
|
|
362
|
+
║ ✅ STATUS: PRODUCTION-READY ║
|
|
363
|
+
║ ║
|
|
364
|
+
║ Every error flows through one place. ║
|
|
365
|
+
║ Every response is consistent. ║
|
|
366
|
+
║ This is where engineering starts. 🎯 ║
|
|
367
|
+
║ ║
|
|
368
|
+
╚═════════════════════════════════════════════════════════════════════════╝`);
|