create-charcole 1.0.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.
Files changed (73) hide show
  1. package/.github/workflows/release.yml +26 -0
  2. package/CHANGELOG.md +25 -0
  3. package/README.md +11 -1
  4. package/bin/index.js +94 -49
  5. package/bin/lib/pkgManager.js +66 -0
  6. package/bin/lib/templateHandler.js +70 -0
  7. package/package.json +4 -1
  8. package/template/js/basePackage.json +28 -0
  9. package/template/{package-lock.json → js/package-lock.json} +1253 -1253
  10. package/template/{package.json → js/package.json} +28 -28
  11. package/template/ts/.env.example +8 -0
  12. package/template/ts/ARCHITECTURE_DIAGRAMS.md +283 -0
  13. package/template/ts/CHECKLIST.md +279 -0
  14. package/template/ts/COMPLETE.md +405 -0
  15. package/template/ts/ERROR_HANDLING.md +393 -0
  16. package/template/ts/IMPLEMENTATION.md +368 -0
  17. package/template/ts/IMPLEMENTATION_COMPLETE.md +363 -0
  18. package/template/ts/INDEX.md +290 -0
  19. package/template/ts/QUICK_REFERENCE.md +270 -0
  20. package/template/ts/README.md +855 -0
  21. package/template/ts/basePackage.json +36 -0
  22. package/template/ts/package-lock.json +2428 -0
  23. package/template/ts/package.json +32 -0
  24. package/template/ts/src/app.js +75 -0
  25. package/template/ts/src/app.ts +66 -0
  26. package/template/ts/src/config/constants.js +20 -0
  27. package/template/ts/src/config/constants.ts +27 -0
  28. package/template/ts/src/config/env.js +26 -0
  29. package/template/ts/src/config/env.ts +40 -0
  30. package/template/ts/src/middlewares/errorHandler.js +180 -0
  31. package/template/ts/src/middlewares/errorHandler.ts +209 -0
  32. package/template/ts/src/middlewares/requestLogger.js +33 -0
  33. package/template/ts/src/middlewares/requestLogger.ts +38 -0
  34. package/template/ts/src/middlewares/validateRequest.js +42 -0
  35. package/template/ts/src/middlewares/validateRequest.ts +46 -0
  36. package/template/ts/src/modules/health/controller.js +50 -0
  37. package/template/ts/src/modules/health/controller.ts +64 -0
  38. package/template/ts/src/routes.js +17 -0
  39. package/template/ts/src/routes.ts +16 -0
  40. package/template/ts/src/server.js +38 -0
  41. package/template/ts/src/server.ts +42 -0
  42. package/template/ts/src/types/express.d.ts +9 -0
  43. package/template/ts/src/utils/AppError.js +182 -0
  44. package/template/ts/src/utils/AppError.ts +220 -0
  45. package/template/ts/src/utils/logger.js +73 -0
  46. package/template/ts/src/utils/logger.ts +55 -0
  47. package/template/ts/src/utils/response.js +51 -0
  48. package/template/ts/src/utils/response.ts +100 -0
  49. package/template/ts/test-api.js +100 -0
  50. package/template/ts/tsconfig.json +19 -0
  51. /package/template/{.env.example → js/.env.example} +0 -0
  52. /package/template/{ARCHITECTURE_DIAGRAMS.md → js/ARCHITECTURE_DIAGRAMS.md} +0 -0
  53. /package/template/{CHECKLIST.md → js/CHECKLIST.md} +0 -0
  54. /package/template/{COMPLETE.md → js/COMPLETE.md} +0 -0
  55. /package/template/{ERROR_HANDLING.md → js/ERROR_HANDLING.md} +0 -0
  56. /package/template/{IMPLEMENTATION.md → js/IMPLEMENTATION.md} +0 -0
  57. /package/template/{IMPLEMENTATION_COMPLETE.md → js/IMPLEMENTATION_COMPLETE.md} +0 -0
  58. /package/template/{INDEX.md → js/INDEX.md} +0 -0
  59. /package/template/{QUICK_REFERENCE.md → js/QUICK_REFERENCE.md} +0 -0
  60. /package/template/{README.md → js/README.md} +0 -0
  61. /package/template/{src → js/src}/app.js +0 -0
  62. /package/template/{src → js/src}/config/constants.js +0 -0
  63. /package/template/{src → js/src}/config/env.js +0 -0
  64. /package/template/{src → js/src}/middlewares/errorHandler.js +0 -0
  65. /package/template/{src → js/src}/middlewares/requestLogger.js +0 -0
  66. /package/template/{src → js/src}/middlewares/validateRequest.js +0 -0
  67. /package/template/{src → js/src}/modules/health/controller.js +0 -0
  68. /package/template/{src → js/src}/routes.js +0 -0
  69. /package/template/{src → js/src}/server.js +0 -0
  70. /package/template/{src → js/src}/utils/AppError.js +0 -0
  71. /package/template/{src → js/src}/utils/logger.js +0 -0
  72. /package/template/{src → js/src}/utils/response.js +0 -0
  73. /package/template/{test-api.js → js/test-api.js} +0 -0
@@ -0,0 +1,393 @@
1
+ # Error Handling Guide
2
+
3
+ This document explains the production-level error handling system in Charcole API.
4
+
5
+ ## Architecture Overview
6
+
7
+ The error handling system is built on a few key principles:
8
+
9
+ 1. **Centralized Error Handling** - All errors flow through one place
10
+ 2. **Error Classification** - Distinguish between operational and programmer errors
11
+ 3. **Consistent Response Format** - All errors return structured JSON
12
+ 4. **Comprehensive Logging** - Full context logged for debugging
13
+
14
+ ## Error Types
15
+
16
+ ### Operational Errors
17
+
18
+ Expected errors that can be handled gracefully. Client caused or known limitations.
19
+
20
+ ```javascript
21
+ import {
22
+ AppError,
23
+ ValidationError,
24
+ AuthenticationError,
25
+ AuthorizationError,
26
+ NotFoundError,
27
+ ConflictError,
28
+ BadRequestError,
29
+ } from "./utils/AppError.js";
30
+
31
+ // Validation error
32
+ throw new ValidationError("Validation failed", [
33
+ { field: "email", message: "Invalid email", code: "invalid_email" },
34
+ ]);
35
+
36
+ // Authentication error
37
+ throw new AuthenticationError("Invalid credentials");
38
+
39
+ // Authorization error
40
+ throw new AuthorizationError("You don't have permission");
41
+
42
+ // Not found error
43
+ throw new NotFoundError("User");
44
+
45
+ // Conflict error (e.g., duplicate email)
46
+ throw new ConflictError("User with this email already exists");
47
+
48
+ // Bad request error
49
+ throw new BadRequestError("Invalid request");
50
+
51
+ // Generic operational error with context
52
+ throw new AppError("Resource quota exceeded", 429, {
53
+ isOperational: true,
54
+ code: "QUOTA_EXCEEDED",
55
+ context: { limit: 100, used: 100 },
56
+ });
57
+ ```
58
+
59
+ ### Programmer Errors
60
+
61
+ Unexpected errors that indicate bugs in the code. These are NOT sent to the client in production.
62
+
63
+ - `SyntaxError` - Code has syntax issues
64
+ - `TypeError` - Type mismatch or undefined method
65
+ - `ReferenceError` - Undefined variable
66
+ - `RangeError` - Invalid range
67
+ - Any unhandled error
68
+
69
+ ## Using asyncHandler
70
+
71
+ Wrap all async route handlers to catch errors automatically:
72
+
73
+ ```javascript
74
+ import { asyncHandler } from "./middlewares/errorHandler.js";
75
+
76
+ // Without asyncHandler - error not caught!
77
+ router.get("/users/:id", (req, res) => {
78
+ const user = await findUser(req.params.id); // Error not caught
79
+ res.json(user);
80
+ });
81
+
82
+ // With asyncHandler - error caught and passed to error handler
83
+ router.get("/users/:id", asyncHandler(async (req, res) => {
84
+ const user = await findUser(req.params.id); // Error caught!
85
+ res.json(user);
86
+ }));
87
+ ```
88
+
89
+ ## Error Handling Flow
90
+
91
+ ```
92
+ Request
93
+
94
+ Route Handler (wrapped with asyncHandler)
95
+
96
+ Error thrown ←─ (if something goes wrong)
97
+
98
+ Global Error Handler Middleware
99
+
100
+ Error normalized (AppError, ZodError, TypeError, etc.)
101
+
102
+ Error logged (operational vs programmer)
103
+
104
+ Response sent (sanitized in production)
105
+ ```
106
+
107
+ ## Logging Behavior
108
+
109
+ ### Operational Errors (isOperational: true)
110
+
111
+ Logged as **WARN** with full context:
112
+
113
+ ```
114
+ [2024-01-19T10:30:00.000Z] WARN: Operational Error: VALIDATION_ERROR
115
+ {
116
+ "type": "OPERATIONAL",
117
+ "code": "VALIDATION_ERROR",
118
+ "message": "Request validation failed",
119
+ "statusCode": 422,
120
+ "method": "POST",
121
+ "path": "/api/items"
122
+ }
123
+ ```
124
+
125
+ ### Programmer Errors (isOperational: false)
126
+
127
+ Logged as **ERROR** with full stack trace:
128
+
129
+ ```
130
+ [2024-01-19T10:30:00.000Z] ERROR: Programmer Error: REFERENCE_ERROR
131
+ {
132
+ "type": "PROGRAMMER",
133
+ "code": "REFERENCE_ERROR",
134
+ "message": "user is not defined",
135
+ "statusCode": 500,
136
+ "method": "GET",
137
+ "path": "/api/users/123"
138
+ }
139
+ ReferenceError: user is not defined
140
+ at getUserHandler (/app/src/modules/users/controller.js:15:3)
141
+ ...
142
+ ```
143
+
144
+ ## Response Format
145
+
146
+ All errors are returned as JSON with this structure:
147
+
148
+ ```json
149
+ {
150
+ "success": false,
151
+ "message": "User not found",
152
+ "code": "NOT_FOUND",
153
+ "statusCode": 404,
154
+ "timestamp": "2024-01-19T10:30:00.000Z"
155
+ }
156
+ ```
157
+
158
+ ### Validation Errors
159
+
160
+ Include detailed field-level errors:
161
+
162
+ ```json
163
+ {
164
+ "success": false,
165
+ "message": "Validation failed",
166
+ "code": "VALIDATION_ERROR",
167
+ "statusCode": 422,
168
+ "errors": [
169
+ {
170
+ "field": "email",
171
+ "message": "Invalid email address",
172
+ "code": "invalid_email"
173
+ },
174
+ {
175
+ "field": "age",
176
+ "message": "Must be at least 18",
177
+ "code": "too_small"
178
+ }
179
+ ],
180
+ "timestamp": "2024-01-19T10:30:00.000Z"
181
+ }
182
+ ```
183
+
184
+ ### Production vs Development
185
+
186
+ **In Production:**
187
+
188
+ - Programmer errors return generic message
189
+ - No stack traces sent to client
190
+ - All errors are logged server-side
191
+
192
+ **In Development:**
193
+
194
+ - Full error details returned
195
+ - Stack traces included
196
+ - Detailed context shown
197
+
198
+ ## Creating Custom Errors
199
+
200
+ Extend AppError for domain-specific errors:
201
+
202
+ ```javascript
203
+ import { AppError } from "./utils/AppError.js";
204
+
205
+ export class UserAlreadyExistsError extends AppError {
206
+ constructor(email) {
207
+ super("User with this email already exists", 409, {
208
+ isOperational: true,
209
+ code: "USER_EXISTS",
210
+ context: { email },
211
+ });
212
+ this.name = "UserAlreadyExistsError";
213
+ }
214
+ }
215
+
216
+ // Use it:
217
+ throw new UserAlreadyExistsError("john@example.com");
218
+ ```
219
+
220
+ ## Error Handling Best Practices
221
+
222
+ ### ✅ DO
223
+
224
+ ```javascript
225
+ // Use asyncHandler for async handlers
226
+ router.get(
227
+ "/users",
228
+ asyncHandler(async (req, res) => {
229
+ const users = await User.find();
230
+ sendSuccess(res, users);
231
+ }),
232
+ );
233
+
234
+ // Throw AppError for operational errors
235
+ if (!user) {
236
+ throw new NotFoundError("User");
237
+ }
238
+
239
+ // Use proper error codes
240
+ throw new ValidationError("Email is required", [
241
+ { field: "email", message: "Email is required", code: "required" },
242
+ ]);
243
+
244
+ // Log important context
245
+ throw new AppError("Payment failed", 402, {
246
+ isOperational: true,
247
+ code: "PAYMENT_FAILED",
248
+ context: { orderId: "123", amount: 99.99 },
249
+ });
250
+ ```
251
+
252
+ ### ❌ DON'T
253
+
254
+ ```javascript
255
+ // Don't use res.status(500).json(...) - use AppError instead
256
+ res.status(500).json({ error: "Something went wrong" });
257
+
258
+ // Don't swallow errors silently
259
+ try {
260
+ await someAsyncWork();
261
+ } catch (error) {
262
+ // WRONG: error is lost!
263
+ }
264
+
265
+ // Don't forget asyncHandler wrapper
266
+ router.get("/users", async (req, res) => {
267
+ // Error not caught!
268
+ const users = await User.find();
269
+ });
270
+
271
+ // Don't mix error handling styles
272
+ try {
273
+ // ...
274
+ } catch (error) {
275
+ res.status(500).json(error); // Use AppError instead
276
+ }
277
+
278
+ // Don't log sensitive data
279
+ logger.error("Error", { password: user.password }); // WRONG!
280
+ ```
281
+
282
+ ## Testing Errors
283
+
284
+ ```javascript
285
+ import { AppError, NotFoundError } from "./utils/AppError.js";
286
+
287
+ describe("Error Handling", () => {
288
+ it("should throw NotFoundError", () => {
289
+ expect(() => {
290
+ throw new NotFoundError("User");
291
+ }).toThrow(NotFoundError);
292
+ });
293
+
294
+ it("should have correct status code", () => {
295
+ const error = new NotFoundError("User");
296
+ expect(error.statusCode).toBe(404);
297
+ expect(error.isOperational).toBe(true);
298
+ });
299
+
300
+ it("should include context in error", () => {
301
+ const error = new AppError("Quote limit exceeded", 429, {
302
+ isOperational: true,
303
+ code: "QUOTE_EXCEEDED",
304
+ context: { limit: 100, used: 100 },
305
+ });
306
+ expect(error.context.limit).toBe(100);
307
+ });
308
+ });
309
+ ```
310
+
311
+ ## Error Codes Reference
312
+
313
+ | Code | Status | Meaning |
314
+ | --------------------- | ------ | --------------------------- |
315
+ | VALIDATION_ERROR | 422 | Request validation failed |
316
+ | NOT_FOUND | 404 | Resource not found |
317
+ | AUTHENTICATION_ERROR | 401 | Invalid credentials |
318
+ | AUTHORIZATION_ERROR | 403 | Permission denied |
319
+ | BAD_REQUEST | 400 | Malformed request |
320
+ | CONFLICT | 409 | Duplicate/conflict resource |
321
+ | INTERNAL_SERVER_ERROR | 500 | Unexpected server error |
322
+
323
+ ## Monitoring & Alerts
324
+
325
+ In production, monitor these metrics:
326
+
327
+ 1. **Error Rate** - Track operational vs programmer errors
328
+ 2. **Response Times** - Identify performance issues
329
+ 3. **5xx Errors** - Alert on programmer errors
330
+ 4. **Specific Error Codes** - Monitor auth failures, validation errors, etc.
331
+
332
+ ## Examples
333
+
334
+ ### Example 1: Creating a User with Validation
335
+
336
+ ```javascript
337
+ import { asyncHandler, ValidationError } from "./middlewares/errorHandler.js";
338
+ import { sendSuccess } from "./utils/response.js";
339
+
340
+ export const createUser = asyncHandler(async (req, res) => {
341
+ const { email, name } = req.validatedData.body;
342
+
343
+ // Check for duplicate
344
+ const exists = await User.findOne({ email });
345
+ if (exists) {
346
+ throw new ConflictError("User with this email already exists", {
347
+ email,
348
+ });
349
+ }
350
+
351
+ // Create user
352
+ const user = await User.create({ email, name });
353
+
354
+ // Return success
355
+ sendSuccess(res, user, 201, "User created successfully");
356
+ });
357
+ ```
358
+
359
+ ### Example 2: Fetching a User with 404 Handling
360
+
361
+ ```javascript
362
+ export const getUser = asyncHandler(async (req, res) => {
363
+ const user = await User.findById(req.params.id);
364
+
365
+ if (!user) {
366
+ throw new NotFoundError("User", { id: req.params.id });
367
+ }
368
+
369
+ sendSuccess(res, user);
370
+ });
371
+ ```
372
+
373
+ ### Example 3: Protected Endpoint with Auth Check
374
+
375
+ ```javascript
376
+ export const updateUser = asyncHandler(async (req, res) => {
377
+ const user = await User.findById(req.params.id);
378
+
379
+ if (!user) {
380
+ throw new NotFoundError("User");
381
+ }
382
+
383
+ // Check authorization
384
+ if (user.id !== req.user.id) {
385
+ throw new AuthorizationError("You can only update your own profile");
386
+ }
387
+
388
+ const updated = await user.updateOne(req.validatedData.body);
389
+ sendSuccess(res, updated, 200, "User updated");
390
+ });
391
+ ```
392
+
393
+ This comprehensive error handling system ensures that every error in your application is caught, logged appropriately, and returned to the client in a consistent, secure manner.