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
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Production-Level Error Handling System - Quick Reference
|
|
2
|
+
|
|
3
|
+
## 🎯 Problem Solved
|
|
4
|
+
|
|
5
|
+
❌ **Before:** Random `res.status(500).json(...)` scattered throughout code
|
|
6
|
+
✅ **After:** Single centralized error handler with distinguished error types
|
|
7
|
+
|
|
8
|
+
## 🏗️ System Architecture
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
Every Request
|
|
12
|
+
↓
|
|
13
|
+
Route Handler (wrapped with asyncHandler)
|
|
14
|
+
↓
|
|
15
|
+
├─→ Success? → sendSuccess(res, data)
|
|
16
|
+
│
|
|
17
|
+
└─→ Error thrown ✘
|
|
18
|
+
↓
|
|
19
|
+
Global Error Handler
|
|
20
|
+
↓
|
|
21
|
+
Normalize Error
|
|
22
|
+
├─→ Is it AppError? Use it
|
|
23
|
+
├─→ Is it ZodError? Convert to ValidationError
|
|
24
|
+
├─→ Is it TypeError/ReferenceError/etc? Convert to InternalServerError
|
|
25
|
+
↓
|
|
26
|
+
Classify Error
|
|
27
|
+
├─→ Operational? (isOperational: true)
|
|
28
|
+
│ ├─ Log as WARN with context
|
|
29
|
+
│ └─ Send full details to client
|
|
30
|
+
│
|
|
31
|
+
└─→ Programmer Error? (isOperational: false)
|
|
32
|
+
├─ Log as ERROR with stack trace
|
|
33
|
+
└─ Send generic message in production
|
|
34
|
+
↓
|
|
35
|
+
Send Consistent JSON Response
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 📦 Error Classes (Use These!)
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
import {
|
|
42
|
+
AppError, // Base class
|
|
43
|
+
ValidationError, // Input validation failed
|
|
44
|
+
AuthenticationError, // Auth failed (401)
|
|
45
|
+
AuthorizationError, // Permission denied (403)
|
|
46
|
+
NotFoundError, // Resource not found (404)
|
|
47
|
+
ConflictError, // Duplicate/conflict (409)
|
|
48
|
+
BadRequestError, // Malformed request (400)
|
|
49
|
+
InternalServerError, // Unexpected error (500)
|
|
50
|
+
} from "./middlewares/errorHandler.js";
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 🎬 Quick Start
|
|
54
|
+
|
|
55
|
+
### 1. Throw Operational Errors
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
// Validation
|
|
59
|
+
throw new ValidationError("Invalid input", [
|
|
60
|
+
{ field: "email", message: "Invalid email", code: "invalid_email" },
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
// Not found
|
|
64
|
+
throw new NotFoundError("User", { id: userId });
|
|
65
|
+
|
|
66
|
+
// Duplicate
|
|
67
|
+
throw new ConflictError("Email already exists", { email });
|
|
68
|
+
|
|
69
|
+
// Auth
|
|
70
|
+
throw new AuthenticationError("Invalid credentials");
|
|
71
|
+
|
|
72
|
+
// Permission
|
|
73
|
+
throw new AuthorizationError("Access denied");
|
|
74
|
+
|
|
75
|
+
// Generic operational error
|
|
76
|
+
throw new AppError("Request failed", 400, {
|
|
77
|
+
isOperational: true,
|
|
78
|
+
code: "CUSTOM_ERROR",
|
|
79
|
+
context: { details: "..." },
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 2. Wrap All Async Handlers
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
import { asyncHandler } from "./middlewares/errorHandler.js";
|
|
87
|
+
|
|
88
|
+
// ✅ Correct
|
|
89
|
+
router.post(
|
|
90
|
+
"/users",
|
|
91
|
+
asyncHandler(async (req, res) => {
|
|
92
|
+
const user = await User.create(req.body);
|
|
93
|
+
sendSuccess(res, user, 201);
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// ❌ Wrong - errors leak!
|
|
98
|
+
router.post("/users", async (req, res) => {
|
|
99
|
+
const user = await User.create(req.body); // Error not caught!
|
|
100
|
+
sendSuccess(res, user, 201);
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 3. Use Validation Middleware
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
import { validateRequest } from "./middlewares/validateRequest.js";
|
|
108
|
+
import { z } from "zod";
|
|
109
|
+
|
|
110
|
+
const createUserSchema = z.object({
|
|
111
|
+
body: z.object({
|
|
112
|
+
email: z.string().email("Invalid email"),
|
|
113
|
+
name: z.string().min(1, "Name required"),
|
|
114
|
+
}),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
router.post(
|
|
118
|
+
"/users",
|
|
119
|
+
validateRequest(createUserSchema),
|
|
120
|
+
asyncHandler(async (req, res) => {
|
|
121
|
+
// req.validatedData.body has been validated
|
|
122
|
+
const user = await User.create(req.validatedData.body);
|
|
123
|
+
sendSuccess(res, user, 201, "User created");
|
|
124
|
+
}),
|
|
125
|
+
);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 📊 Logging Behavior
|
|
129
|
+
|
|
130
|
+
| Error Type | Level | Includes Stack | Info Sent to Client |
|
|
131
|
+
| ---------------------- | ----- | -------------- | ---------------------- |
|
|
132
|
+
| ValidationError | WARN | ❌ | ✅ All details |
|
|
133
|
+
| NotFoundError | WARN | ❌ | ✅ All details |
|
|
134
|
+
| AuthenticationError | WARN | ❌ | ✅ All details |
|
|
135
|
+
| TypeError (programmer) | ERROR | ✅ | ❌ Generic only (prod) |
|
|
136
|
+
| Unhandled (programmer) | FATAL | ✅ | ❌ Generic only (prod) |
|
|
137
|
+
|
|
138
|
+
## 📋 Response Format
|
|
139
|
+
|
|
140
|
+
All responses are consistent JSON:
|
|
141
|
+
|
|
142
|
+
### Success
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"success": true,
|
|
147
|
+
"message": "User created successfully",
|
|
148
|
+
"data": { "id": "123", "name": "John" },
|
|
149
|
+
"timestamp": "2024-01-19T15:55:30.000Z"
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Operational Error
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"success": false,
|
|
158
|
+
"message": "User not found",
|
|
159
|
+
"code": "NOT_FOUND",
|
|
160
|
+
"statusCode": 404,
|
|
161
|
+
"context": { "id": "999" },
|
|
162
|
+
"timestamp": "2024-01-19T15:55:30.000Z"
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Validation Error
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"success": false,
|
|
171
|
+
"message": "Validation failed",
|
|
172
|
+
"code": "VALIDATION_ERROR",
|
|
173
|
+
"statusCode": 422,
|
|
174
|
+
"errors": [
|
|
175
|
+
{ "field": "email", "message": "Invalid email", "code": "invalid_email" }
|
|
176
|
+
],
|
|
177
|
+
"timestamp": "2024-01-19T15:55:30.000Z"
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Programmer Error (Production)
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"success": false,
|
|
186
|
+
"message": "Internal server error",
|
|
187
|
+
"code": "INTERNAL_SERVER_ERROR",
|
|
188
|
+
"timestamp": "2024-01-19T15:55:30.000Z"
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## 🛡️ Golden Rules
|
|
193
|
+
|
|
194
|
+
1. **Always use asyncHandler** for async route handlers
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
router.get("/endpoint", asyncHandler(async (req, res) => { ... }))
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
2. **Never use res.status().json()** for errors - throw AppError instead
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
// ❌ Wrong
|
|
204
|
+
res.status(400).json({ error: "..." });
|
|
205
|
+
|
|
206
|
+
// ✅ Correct
|
|
207
|
+
throw new BadRequestError("Invalid input");
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
3. **Validate request early** with validateRequest middleware
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
router.post("/endpoint", validateRequest(schema), handler);
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
4. **Throw, don't catch** - let global handler catch
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
// ❌ Wrong
|
|
220
|
+
try {
|
|
221
|
+
await someTask();
|
|
222
|
+
} catch (err) {
|
|
223
|
+
res.status(500).json(err);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ✅ Correct
|
|
227
|
+
await someTask(); // Error bubbles up to global handler
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
5. **Include context** when throwing errors
|
|
231
|
+
```javascript
|
|
232
|
+
throw new NotFoundError("User", { id: userId, email: userEmail });
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## 🚀 Features at a Glance
|
|
236
|
+
|
|
237
|
+
| Feature | Status |
|
|
238
|
+
| ------------------------------------------------ | ------ |
|
|
239
|
+
| Centralized error handler | ✅ |
|
|
240
|
+
| Error classification (operational vs programmer) | ✅ |
|
|
241
|
+
| Validation error formatting | ✅ |
|
|
242
|
+
| Async error wrapping | ✅ |
|
|
243
|
+
| Stack trace logging | ✅ |
|
|
244
|
+
| Production sanitization | ✅ |
|
|
245
|
+
| Request logging | ✅ |
|
|
246
|
+
| Graceful shutdown | ✅ |
|
|
247
|
+
| Unhandled exception catching | ✅ |
|
|
248
|
+
| Consistent JSON responses | ✅ |
|
|
249
|
+
|
|
250
|
+
## 📚 Full Documentation
|
|
251
|
+
|
|
252
|
+
- **[ERROR_HANDLING.md](ERROR_HANDLING.md)** - Comprehensive guide with examples
|
|
253
|
+
- **[IMPLEMENTATION_COMPLETE.md](IMPLEMENTATION_COMPLETE.md)** - Full implementation details
|
|
254
|
+
|
|
255
|
+
## 🎓 Key Files
|
|
256
|
+
|
|
257
|
+
| File | Purpose |
|
|
258
|
+
| ------------------------------------ | ----------------------------------- |
|
|
259
|
+
| `src/utils/AppError.js` | Error class hierarchy |
|
|
260
|
+
| `src/middlewares/errorHandler.js` | Global error handler + asyncHandler |
|
|
261
|
+
| `src/middlewares/validateRequest.js` | Zod validation middleware |
|
|
262
|
+
| `src/middlewares/requestLogger.js` | Request logging |
|
|
263
|
+
| `src/utils/logger.js` | Structured logging with colors |
|
|
264
|
+
| `src/utils/response.js` | Success response helpers |
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
**That's it! Your API now has enterprise-grade error handling.** 🎯
|
|
269
|
+
|
|
270
|
+
Every error flows through one place. Every response is consistent. Errors are properly classified and logged. You're ready for production. 🚀
|