create-charcole 2.2.0 → 2.2.2

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 (77) hide show
  1. package/.github/workflows/release.yml +26 -26
  2. package/CHANGELOG.md +301 -301
  3. package/LICENSE +21 -21
  4. package/README.md +357 -354
  5. package/bin/index.js +494 -444
  6. package/bin/lib/pkgManager.js +49 -49
  7. package/bin/lib/templateHandler.js +33 -33
  8. package/package.json +42 -27
  9. package/packages/swagger/BACKWARD_COMPATIBILITY.md +1 -1
  10. package/packages/swagger/CHANGELOG.md +1 -1
  11. package/packages/swagger/README.md +3 -3
  12. package/packages/swagger/package.json +57 -44
  13. package/packages/swagger/src/index.d.ts +126 -126
  14. package/packages/swagger/src/index.js +12 -12
  15. package/packages/swagger/src/setup.js +100 -100
  16. package/template/js/.env.example +16 -15
  17. package/template/js/README.md +982 -978
  18. package/template/js/basePackage.json +26 -26
  19. package/template/js/src/app.js +81 -81
  20. package/template/js/src/config/constants.js +20 -20
  21. package/template/js/src/config/env.js +26 -26
  22. package/template/js/src/config/swagger.config.js +15 -15
  23. package/template/js/src/lib/swagger/SWAGGER_GUIDE.md +3 -3
  24. package/template/js/src/middlewares/errorHandler.js +180 -180
  25. package/template/js/src/middlewares/requestLogger.js +33 -33
  26. package/template/js/src/middlewares/validateRequest.js +42 -42
  27. package/template/js/src/modules/auth/auth.constants.js +3 -3
  28. package/template/js/src/modules/auth/auth.controller.js +29 -29
  29. package/template/js/src/modules/auth/auth.middlewares.js +19 -19
  30. package/template/js/src/modules/auth/auth.routes.js +131 -131
  31. package/template/js/src/modules/auth/auth.schemas.js +60 -60
  32. package/template/js/src/modules/auth/auth.service.js +67 -67
  33. package/template/js/src/modules/auth/package.json +6 -6
  34. package/template/js/src/modules/health/controller.js +151 -151
  35. package/template/js/src/modules/swagger/package.json +5 -5
  36. package/template/js/src/repositories/user.repo.js +19 -19
  37. package/template/js/src/routes/index.js +25 -25
  38. package/template/js/src/routes/protected.js +57 -57
  39. package/template/js/src/server.js +38 -38
  40. package/template/js/src/utils/AppError.js +182 -182
  41. package/template/js/src/utils/logger.js +73 -73
  42. package/template/js/src/utils/response.js +51 -51
  43. package/template/ts/.env.example +16 -15
  44. package/template/ts/README.md +982 -978
  45. package/template/ts/basePackage.json +36 -36
  46. package/template/ts/build.js +46 -46
  47. package/template/ts/src/app.ts +71 -71
  48. package/template/ts/src/config/constants.ts +27 -27
  49. package/template/ts/src/config/env.ts +40 -40
  50. package/template/ts/src/config/swagger.config.ts +30 -30
  51. package/template/ts/src/lib/swagger/SWAGGER_GUIDE.md +2 -2
  52. package/template/ts/src/middlewares/errorHandler.ts +201 -201
  53. package/template/ts/src/middlewares/requestLogger.ts +38 -38
  54. package/template/ts/src/middlewares/validateRequest.ts +46 -46
  55. package/template/ts/src/modules/auth/auth.constants.ts +6 -6
  56. package/template/ts/src/modules/auth/auth.controller.ts +32 -32
  57. package/template/ts/src/modules/auth/auth.middlewares.ts +46 -46
  58. package/template/ts/src/modules/auth/auth.routes.ts +52 -52
  59. package/template/ts/src/modules/auth/auth.schemas.ts +73 -73
  60. package/template/ts/src/modules/auth/auth.service.ts +106 -106
  61. package/template/ts/src/modules/auth/package.json +10 -10
  62. package/template/ts/src/modules/health/controller.ts +80 -80
  63. package/template/ts/src/modules/swagger/package.json +5 -5
  64. package/template/ts/src/repositories/user.repo.ts +33 -33
  65. package/template/ts/src/routes/index.ts +24 -24
  66. package/template/ts/src/routes/protected.ts +46 -46
  67. package/template/ts/src/server.ts +41 -41
  68. package/template/ts/src/types/express.d.ts +9 -9
  69. package/template/ts/src/utils/AppError.ts +220 -220
  70. package/template/ts/src/utils/logger.ts +55 -55
  71. package/template/ts/src/utils/response.ts +100 -100
  72. package/template/ts/tsconfig.json +26 -26
  73. package/packages/swagger/package-lock.json +0 -1715
  74. package/tmpclaude-1049-cwd +0 -1
  75. package/tmpclaude-3e37-cwd +0 -1
  76. package/tmpclaude-4d73-cwd +0 -1
  77. package/tmpclaude-8a8e-cwd +0 -1
@@ -1,978 +1,982 @@
1
- # Getting Started with Charcole API
2
-
3
- Welcome! This guide will help you set up and start using the Charcole API framework.
4
-
5
- ## 📚 Table of Contents
6
-
7
- 1. [Installation](#installation)
8
- 2. [Project Structure](#project-structure)
9
- 3. [Configuration](#configuration)
10
- 4. [Creating Your First Endpoint](#creating-your-first-endpoint)
11
- 5. [API Documentation with Swagger](#api-documentation-with-swagger)
12
- 6. [Error Handling](#error-handling)
13
- 7. [Validation](#validation)
14
- 8. [Logging](#logging)
15
- 9. [Running Your API](#running-your-api)
16
- 10. [Troubleshooting](#troubleshooting)
17
-
18
- ---
19
-
20
- ## 🔧 Installation
21
-
22
- ### Prerequisites
23
-
24
- - Node.js 18+ ([Download](https://nodejs.org/))
25
- - npm or yarn
26
-
27
- ### Setup Steps
28
-
29
- 1. **Create Charcole App**
30
-
31
- ```bash
32
- npx create-charcole@latest charcole-demo
33
- ```
34
-
35
- 2. **Create environment file**
36
-
37
- ```bash
38
- cp .env.example .env
39
- ```
40
-
41
- 3. **Run the charcole**
42
- ```bash
43
- npm run dev
44
- ```
45
-
46
- You should see:
47
-
48
- ```
49
- [2024-01-20T12:00:00.000Z] INFO: Express app configured successfully
50
- [2024-01-20T12:00:00.000Z] INFO: 🔥 Server running in development mode
51
- ```
52
-
53
- ---
54
-
55
- ## 📂 Project Structure
56
-
57
- Understanding the folder structure:
58
-
59
- ```
60
- charcole-demo/
61
- ├── src/ # All application code
62
- │ ├── config/
63
- │ │ ├── env.js # Environment validation
64
- │ │ │ # Validates all .env variables at startup
65
- │ │ │ # If invalid, server won't start
66
- │ │
67
- │ │ └── constants.js # HTTP status codes & error messages
68
- │ │ # Use these in your code
69
- │ │
70
- ├── middlewares/ # Express middleware
71
- │ │ ├── errorHandler.js # IMPORTANT: Global error handler
72
- │ │# Every error flows through here
73
- │ │ │ # Also exports: asyncHandler, AppError classes
74
- │ │
75
- │ │ ├── validateRequest.js # Validates request body, query, params
76
- │ │ │ # Uses Zod schemas
77
- │ │ │ # Throws ValidationError if invalid
78
- │ │ │
79
- │ │ └── requestLogger.js # Logs all incoming requests
80
- │ │ # Logs: method, path, status, duration, IP
81
- │ │
82
- ├── modules/ # Feature modules (organized by feature)
83
- │ │ └── health/
84
- │ │ ├── controller.js # Route handlers for this feature
85
- │ │ │ # Export: getHealth, createItem, etc.
86
- │ │ # Export: validation schemas
87
- │ │
88
- │ │ └── service.js # (Optional) Business logic
89
- │ │ └── model.js # (Optional) Data models
90
- │ │
91
- ├── utils/
92
- │ │ ├── AppError.js # IMPORTANT: Error class hierarchy
93
- │ │# Use these to throw errors
94
- │ │ │ # ValidationError, NotFoundError, etc.
95
- │ │
96
- │ │ ├── logger.js # Structured logging
97
- │ │ │ # Use: logger.info(), logger.error(), etc.
98
- │ │ │
99
- │ │ └── response.js # Response helpers
100
- │ │ # Use: sendSuccess() for responses
101
- │ │
102
- ├── app.js # Express app configuration
103
- │ │ # All middleware setup
104
- │ │ # Error handler registered here (last)
105
- │ │
106
- │ ├── routes.js # All API routes
107
- │ │ # Import handlers from modules
108
- │ │ # Define routes here
109
- │ │
110
- └── server.js # Server entry point
111
- # Listen on PORT
112
- # Graceful shutdown handling
113
-
114
- ├── .env # Environment variables (GITIGNORED)
115
- ├── .env.example # Example env variables (committed)
116
- ├── package.json # Dependencies & scripts
117
- ├── README.md # Project overview (for GitHub/npm)
118
- └── template/README.md # This file - Getting started guide
119
- ```
120
-
121
- ### Creating New Modules
122
-
123
- Create a new feature:
124
-
125
- ```bash
126
- # Create directory
127
- mkdir -p src/modules/users
128
-
129
- # Create files
130
- touch src/modules/users/controller.js
131
- touch src/modules/users/service.js
132
- ```
133
-
134
- **controller.js** - Route handlers
135
-
136
- ```javascript
137
- import { asyncHandler } from "../../middlewares/errorHandler.js";
138
- import { sendSuccess } from "../../utils/response.js";
139
- import { z } from "zod";
140
-
141
- export const getUserSchema = z.object({
142
- params: z.object({ id: z.string() }),
143
- });
144
-
145
- export const getUser = asyncHandler(async (req, res) => {
146
- const { id } = req.params;
147
- const user = await findUserById(id);
148
- if (!user) throw new NotFoundError("User");
149
- sendSuccess(res, user);
150
- });
151
- ```
152
-
153
- ---
154
-
155
- ## ⚙️ Configuration
156
-
157
- ### Environment Variables
158
-
159
- Edit `.env`:
160
-
161
- ```env
162
- # Server
163
- NODE_ENV=development # development, production, or test
164
- PORT=3000 # Server port
165
-
166
- # Logging
167
- LOG_LEVEL=info # debug, info, warn, error
168
- # In production, use warn or error
169
-
170
- # CORS
171
- CORS_ORIGIN=* # Change to your domain in production
172
- # Example: https://myapp.com
173
-
174
- # Timeouts
175
- REQUEST_TIMEOUT=30000 # 30 seconds
176
- ```
177
-
178
- ### Using Environment Variables
179
-
180
- ```javascript
181
- import { env } from "./config/env.js";
182
-
183
- console.log(env.PORT); // 3000
184
- console.log(env.NODE_ENV); // development
185
- console.log(env.isProduction); // false
186
- console.log(env.isDevelopment); // true
187
- ```
188
-
189
- ---
190
-
191
- ## 🚀 Creating Your First Endpoint
192
-
193
- ### Step 1: Create Controller
194
-
195
- **src/modules/posts/controller.js**
196
-
197
- ```javascript
198
- import { z } from "zod";
199
- import { asyncHandler } from "../../middlewares/errorHandler.js";
200
- import { validateRequest } from "../../middlewares/validateRequest.js";
201
- import { sendSuccess } from "../../utils/response.js";
202
- import { NotFoundError } from "../../middlewares/errorHandler.js";
203
-
204
- // Define validation schema
205
- export const createPostSchema = z.object({
206
- body: z.object({
207
- title: z.string().min(1, "Title required").max(200),
208
- content: z.string().min(1, "Content required"),
209
- }),
210
- });
211
-
212
- // Define handler
213
- export const createPost = asyncHandler(async (req, res) => {
214
- const { title, content } = req.validatedData.body;
215
-
216
- // Your logic here
217
- const post = {
218
- id: "1",
219
- title,
220
- content,
221
- createdAt: new Date(),
222
- };
223
-
224
- sendSuccess(res, post, 201, "Post created successfully");
225
- });
226
-
227
- export const getPost = asyncHandler(async (req, res) => {
228
- const { id } = req.params;
229
-
230
- // Simulate database fetch
231
- if (id !== "1") {
232
- throw new NotFoundError("Post", { id });
233
- }
234
-
235
- const post = { id: "1", title: "Hello", content: "World" };
236
- sendSuccess(res, post);
237
- });
238
- ```
239
-
240
- ### Step 2: Register Routes
241
-
242
- **src/routes.js**
243
-
244
- ```javascript
245
- import { Router } from "express";
246
- import { getHealth, createItem } from "./modules/health/controller.js";
247
- import {
248
- createPost,
249
- getPost,
250
- createPostSchema,
251
- } from "./modules/posts/controller.js";
252
- import { validateRequest } from "./middlewares/validateRequest.js";
253
-
254
- const router = Router();
255
-
256
- // Health check
257
- router.get("/health", getHealth);
258
-
259
- // Posts
260
- router.post("/posts", validateRequest(createPostSchema), createPost);
261
- router.get("/posts/:id", getPost);
262
-
263
- export default router;
264
- ```
265
-
266
- ### Step 3: Test Your Endpoint
267
-
268
- ```bash
269
- # Start server
270
- npm run dev
271
-
272
- # Test creation
273
- curl -X POST http://localhost:3000/api/posts \
274
- -H "Content-Type: application/json" \
275
- -d '{"title":"My Post","content":"Hello World"}'
276
-
277
- # Test retrieval
278
- curl http://localhost:3000/api/posts/1
279
-
280
- # Test 404
281
- curl http://localhost:3000/api/posts/999
282
- ```
283
-
284
- ---
285
-
286
- ## 🛡️ Error Handling
287
-
288
- ### Understanding Errors
289
-
290
- There are **two types of errors**:
291
-
292
- #### 1. Operational Errors (Expected)
293
-
294
- User/input errors that can be handled gracefully.
295
-
296
- ```javascript
297
- import {
298
- ValidationError, // 422 - Input validation failed
299
- BadRequestError, // 400 - Malformed request
300
- AuthenticationError, // 401 - Invalid credentials
301
- AuthorizationError, // 403 - Permission denied
302
- NotFoundError, // 404 - Resource not found
303
- ConflictError, // 409 - Duplicate/conflict
304
- AppError, // Generic error
305
- } from "./middlewares/errorHandler.js";
306
-
307
- // Throw operational errors
308
- throw new NotFoundError("User", { id: userId });
309
- throw new ConflictError("Email already exists");
310
- throw new AuthenticationError("Invalid password");
311
- ```
312
-
313
- #### 2. Programmer Errors (Bugs)
314
-
315
- Unexpected errors that indicate code issues.
316
-
317
- ```javascript
318
- // These are automatically caught and handled:
319
- // - TypeError
320
- // - ReferenceError
321
- // - SyntaxError
322
- // - Any unhandled error
323
-
324
- // Example: This is caught automatically
325
- const user = null;
326
- user.name; // TypeError: Cannot read property 'name' of null
327
- // → Logged as ERROR with stack trace
328
- // Generic response sent to client
329
- ```
330
-
331
- ### Using asyncHandler
332
-
333
- **ALWAYS wrap async handlers** to catch errors:
334
-
335
- ```javascript
336
- // ✅ CORRECT - Error is caught
337
- router.get(
338
- "/users/:id",
339
- asyncHandler(async (req, res) => {
340
- const user = await User.findById(req.params.id); // Error caught
341
- if (!user) throw new NotFoundError("User");
342
- sendSuccess(res, user);
343
- }),
344
- );
345
-
346
- // ❌ WRONG - Error NOT caught!
347
- router.get("/users/:id", async (req, res) => {
348
- const user = await User.findById(req.params.id); // Error leaks!
349
- sendSuccess(res, user);
350
- });
351
- ```
352
-
353
- ### Response Format
354
-
355
- **Success (200):**
356
-
357
- ```json
358
- {
359
- "success": true,
360
- "message": "User created successfully",
361
- "data": { "id": "123", "name": "John" },
362
- "timestamp": "2024-01-20T12:00:00.000Z"
363
- }
364
- ```
365
-
366
- **Validation Error (422):**
367
-
368
- ```json
369
- {
370
- "success": false,
371
- "message": "Validation failed",
372
- "code": "VALIDATION_ERROR",
373
- "statusCode": 422,
374
- "errors": [
375
- {
376
- "field": "email",
377
- "message": "Invalid email address",
378
- "code": "invalid_email"
379
- }
380
- ],
381
- "timestamp": "2024-01-20T12:00:00.000Z"
382
- }
383
- ```
384
-
385
- **Not Found (404):**
386
-
387
- ```json
388
- {
389
- "success": false,
390
- "message": "User not found",
391
- "code": "NOT_FOUND",
392
- "statusCode": 404,
393
- "context": { "id": "999" },
394
- "timestamp": "2024-01-20T12:00:00.000Z"
395
- }
396
- ```
397
-
398
- ---
399
-
400
- ## 📖 API Documentation with Swagger
401
-
402
- Your API comes with **automatic interactive documentation** powered by Swagger UI!
403
-
404
- ### Accessing the Documentation
405
-
406
- 1. Start your server: `npm run dev`
407
- 2. Visit: **http://localhost:3000/api-docs**
408
-
409
- You'll see all your APIs automatically documented and can test them directly from the browser!
410
-
411
- ### How It Works
412
-
413
- All built-in APIs are already documented. When you create new endpoints, simply add JSDoc comments with `@swagger` annotations:
414
-
415
- ```javascript
416
- /**
417
- * @swagger
418
- * /api/users:
419
- * get:
420
- * summary: Get all users
421
- * tags:
422
- * - Users
423
- * responses:
424
- * 200:
425
- * description: Success
426
- */
427
- export const getAllUsers = asyncHandler(async (req, res) => {
428
- // Your code here
429
- });
430
- ```
431
-
432
- That's it! Your new endpoint will automatically appear in Swagger UI.
433
-
434
- ### Quick Example: Documenting a POST Endpoint
435
-
436
- ```javascript
437
- /**
438
- * @swagger
439
- * /api/posts:
440
- * post:
441
- * summary: Create a new post
442
- * tags:
443
- * - Posts
444
- * requestBody:
445
- * required: true
446
- * content:
447
- * application/json:
448
- * schema:
449
- * type: object
450
- * required:
451
- * - title
452
- * properties:
453
- * title:
454
- * type: string
455
- * example: My First Post
456
- * content:
457
- * type: string
458
- * example: This is the post content
459
- * responses:
460
- * 201:
461
- * description: Post created successfully
462
- * 400:
463
- * description: Validation error
464
- */
465
- export const createPost = asyncHandler(async (req, res) => {
466
- // Your implementation
467
- });
468
- ```
469
-
470
- ### Protected Endpoints (with Authentication)
471
-
472
- For endpoints that require authentication, add the `security` field:
473
-
474
- ```javascript
475
- /**
476
- * @swagger
477
- * /api/profile:
478
- * get:
479
- * summary: Get user profile
480
- * tags:
481
- * - Profile
482
- * security:
483
- * - bearerAuth: []
484
- * responses:
485
- * 200:
486
- * description: Profile retrieved
487
- * 401:
488
- * description: Unauthorized
489
- */
490
- router.get("/profile", requireAuth, getProfile);
491
- ```
492
-
493
- ### 📘 Complete Guide
494
-
495
- For comprehensive examples including:
496
-
497
- - Path and query parameters
498
- - File uploads
499
- - Complex schemas
500
- - Error responses
501
- - CRUD operations
502
-
503
- See the **[Complete Swagger Documentation Guide](src/lib/swagger/SWAGGER_GUIDE.md)**
504
-
505
- ### Testing APIs in Swagger UI
506
-
507
- 1. Open http://localhost:3000/api-docs
508
- 2. Click on any endpoint to expand it
509
- 3. Click "Try it out"
510
- 4. Fill in the parameters
511
- 5. Click "Execute"
512
- 6. See the response!
513
-
514
- For protected endpoints:
515
-
516
- 1. Click the "Authorize" button at the top
517
- 2. Enter your JWT token
518
- 3. Now you can test protected endpoints
519
-
520
- ---
521
-
522
- ## ✔️ Validation
523
-
524
- ### Zod Schema Basics
525
-
526
- ```javascript
527
- import { z } from "zod";
528
-
529
- // Define schema
530
- const userSchema = z.object({
531
- body: z.object({
532
- email: z.string().email("Invalid email"),
533
- name: z.string().min(1, "Name required"),
534
- age: z.number().min(18, "Must be 18+").optional(),
535
- }),
536
- query: z.object({
537
- page: z.coerce.number().default(1),
538
- }),
539
- });
540
-
541
- // Use in route
542
- router.post("/users", validateRequest(userSchema), handler);
543
- ```
544
-
545
- ### Common Validations
546
-
547
- ```javascript
548
- // String
549
- z.string().min(1, "Required");
550
- z.string().email("Invalid email");
551
- z.string().url("Invalid URL");
552
- z.string().regex(/^\d+$/, "Numbers only");
553
-
554
- // Number
555
- z.number().min(0).max(100);
556
- z.coerce.number(); // Convert string to number
557
-
558
- // Array
559
- z.array(z.string());
560
- z.array(z.object({ id: z.string() }));
561
-
562
- // Object
563
- z.object({ key: z.string() });
564
-
565
- // Union
566
- z.union([z.string(), z.number()]);
567
-
568
- // Optional
569
- z.string().optional();
570
- z.string().default("value");
571
-
572
- // Enum
573
- z.enum(["active", "inactive"]);
574
- ```
575
-
576
- ---
577
-
578
- ## 📝 Logging
579
-
580
- ### Using Logger
581
-
582
- ```javascript
583
- import { logger } from "./utils/logger.js";
584
-
585
- // Different log levels
586
- logger.debug("Detailed debug info", { data: true });
587
- logger.info("Important information", { userId: 123 });
588
- logger.warn("Warning - something unexpected", { statusCode: 404 });
589
- logger.error("Error occurred", { error: "message" }, stack);
590
- ```
591
-
592
- ### Log Output
593
-
594
- **Development** (colorized):
595
-
596
- ```
597
- [2024-01-20T12:00:00.000Z] DEBUG: Debug message
598
- [2024-01-20T12:00:00.000Z] INFO: Request processed
599
- [2024-01-20T12:00:00.000Z] WARN: User not found
600
- [2024-01-20T12:00:00.000Z] ERROR: Database error
601
- ```
602
-
603
- **Production** (to file/service):
604
-
605
- ```
606
- {"level":"info","message":"Request processed","timestamp":"2024-01-20T12:00:00.000Z"}
607
- {"level":"error","message":"Database error","timestamp":"2024-01-20T12:00:00.000Z"}
608
- ```
609
-
610
- ---
611
-
612
- ## 🎯 Running Your API
613
-
614
- ### Development
615
-
616
- ```bash
617
- # Start with auto-reload
618
- npm run dev
619
-
620
- # Output:
621
- # [2024-01-20T12:00:00.000Z] INFO: Express app configured successfully
622
- # [2024-01-20T12:00:00.000Z] INFO: 🔥 Server running in development mode
623
- # { "url": "http://localhost:3000", "port": 3000 }
624
- ```
625
-
626
- ### Production
627
-
628
- ```bash
629
- # Set environment
630
- export NODE_ENV=production
631
-
632
- # Start server
633
- npm start
634
- ```
635
-
636
- ### Testing
637
-
638
- ```bash
639
- # Test API endpoints
640
- node test-api.js
641
- ```
642
-
643
- ---
644
-
645
- ## 🆘 Troubleshooting
646
-
647
- ### Server won't start
648
-
649
- **Error:** `listen EADDRINUSE: address already in use :::3000`
650
-
651
- **Solution:**
652
-
653
- ```bash
654
- # Kill process on port 3000
655
- # macOS/Linux:
656
- lsof -ti:3000 | xargs kill -9
657
-
658
- # Windows PowerShell:
659
- Get-Process | Where-Object {$_.Port -eq 3000} | Stop-Process -Force
660
- ```
661
-
662
- ### Validation errors not working
663
-
664
- **Ensure you:**
665
-
666
- 1. Import `validateRequest` from middlewares
667
- 2. Add it as middleware before handler
668
- 3. Pass schema with body/query/params structure
669
- 4. Use `req.validatedData` in handler
670
-
671
- ```javascript
672
- // Correct
673
- const schema = z.object({
674
- body: z.object({ name: z.string() }),
675
- });
676
- router.post("/items", validateRequest(schema), handler);
677
-
678
- // In handler:
679
- const { name } = req.validatedData.body;
680
- ```
681
-
682
- ### Errors not being caught
683
-
684
- **Ensure you:**
685
-
686
- 1. Wrap handler with `asyncHandler`
687
- 2. Throw AppError instances
688
- 3. Don't try-catch to return res.status()
689
-
690
- ```javascript
691
- // Correct
692
- router.get(
693
- "/items/:id",
694
- asyncHandler(async (req, res) => {
695
- const item = await Item.findById(req.params.id);
696
- if (!item) throw new NotFoundError("Item");
697
- sendSuccess(res, item);
698
- }),
699
- );
700
- ```
701
-
702
- ### Environment variables not loading
703
-
704
- **Check:**
705
-
706
- 1. `.env` file exists in root
707
- 2. Variable names match (case-sensitive)
708
- 3. Restart server after changing .env
709
- 4. Use `env.VARIABLE_NAME` to access
710
-
711
- ```javascript
712
- import { env } from "./config/env.js";
713
-
714
- console.log(env.PORT); // Will be validated at startup
715
- ```
716
-
717
- ### CORS errors
718
-
719
- **In development**, CORS should be `*`:
720
-
721
- ```env
722
- CORS_ORIGIN=*
723
- ```
724
-
725
- **In production**, set your domain:
726
-
727
- ```env
728
- CORS_ORIGIN=https://myapp.com
729
- ```
730
-
731
- ---
732
-
733
- ## 📖 Next Steps
734
-
735
- 1. **Read:** [Main README](../README.md) - Project overview
736
- 2. **Learn:** [Quick Reference](../QUICK_REFERENCE.md) - Patterns & rules
737
- 3. **Deep Dive:** [Error Handling Guide](../ERROR_HANDLING.md) - Full documentation
738
- 4. **Build:** Create your first module using examples above
739
- 5. **Deploy:** Follow production checklist in README
740
-
741
- ---
742
-
743
- ## 🤔 Common Patterns
744
-
745
- ### Create with Validation
746
-
747
- ```javascript
748
- const createSchema = z.object({
749
- body: z.object({
750
- name: z.string().min(1),
751
- email: z.string().email(),
752
- }),
753
- });
754
-
755
- export const create = asyncHandler(async (req, res) => {
756
- const { name, email } = req.validatedData.body;
757
- const item = await Item.create({ name, email });
758
- sendSuccess(res, item, 201, "Created");
759
- });
760
-
761
- router.post("/items", validateRequest(createSchema), create);
762
- ```
763
-
764
- ### Get with 404 Handling
765
-
766
- ```javascript
767
- export const getById = asyncHandler(async (req, res) => {
768
- const item = await Item.findById(req.params.id);
769
- if (!item) throw new NotFoundError("Item");
770
- sendSuccess(res, item);
771
- });
772
-
773
- router.get("/items/:id", getById);
774
- ```
775
-
776
- ### Update with Validation
777
-
778
- ```javascript
779
- const updateSchema = z.object({
780
- params: z.object({ id: z.string() }),
781
- body: z.object({
782
- name: z.string().min(1).optional(),
783
- email: z.string().email().optional(),
784
- }),
785
- });
786
-
787
- export const update = asyncHandler(async (req, res) => {
788
- const item = await Item.findByIdAndUpdate(
789
- req.params.id,
790
- req.validatedData.body,
791
- );
792
- if (!item) throw new NotFoundError("Item");
793
- sendSuccess(res, item, 200, "Updated");
794
- });
795
-
796
- router.patch("/items/:id", validateRequest(updateSchema), update);
797
- ```
798
-
799
- ### Delete with 404 Handling
800
-
801
- ```javascript
802
- export const delete = asyncHandler(async (req, res) => {
803
- const item = await Item.findByIdAndDelete(req.params.id);
804
- if (!item) throw new NotFoundError("Item");
805
- sendSuccess(res, { id: item.id }, 200, "Deleted");
806
- });
807
-
808
- router.delete("/items/:id", delete);
809
- ```
810
-
811
- ---
812
-
813
- **You're ready to build!** 🚀
814
-
815
- Questions? Check the [Full Documentation](../ERROR_HANDLING.md).
816
- PORT=3000 # Server port
817
- LOG_LEVEL=info # debug, info, warn, error
818
- CORS_ORIGIN=\* # CORS origin
819
- REQUEST_TIMEOUT=30000 # Request timeout in ms
820
-
821
- ````
822
-
823
- ## Running
824
-
825
- **Development** (with auto-reload):
826
-
827
- ```bash
828
- npm run dev
829
- ````
830
-
831
- **Production**:
832
-
833
- ```bash
834
- npm start
835
- ```
836
-
837
- ## API Endpoints
838
-
839
- ### Health Check
840
-
841
- ```
842
- GET /health
843
- ```
844
-
845
- Response:
846
-
847
- ```json
848
- {
849
- "success": true,
850
- "message": "Success",
851
- "data": {
852
- "status": "healthy",
853
- "uptime": 42.123,
854
- "timestamp": "2024-01-19T10:30:00.000Z"
855
- },
856
- "timestamp": "2024-01-19T10:30:00.000Z"
857
- }
858
- ```
859
-
860
- ### Create Item (Example with Validation)
861
-
862
- ```
863
- POST /api/items
864
- Content-Type: application/json
865
-
866
- {
867
- "name": "Example Item",
868
- "description": "Optional description"
869
- }
870
- ```
871
-
872
- ## Response Format
873
-
874
- All API responses follow a consistent format:
875
-
876
- **Success**:
877
-
878
- ```json
879
- {
880
- "success": true,
881
- "message": "Success message",
882
- "data": {},
883
- "timestamp": "2024-01-19T10:30:00.000Z"
884
- }
885
- ```
886
-
887
- **Error**:
888
-
889
- ```json
890
- {
891
- "success": false,
892
- "message": "Error message",
893
- "timestamp": "2024-01-19T10:30:00.000Z"
894
- }
895
- ```
896
-
897
- **Validation Error**:
898
-
899
- ```json
900
- {
901
- "success": false,
902
- "message": "Validation failed",
903
- "errors": [
904
- {
905
- "field": "name",
906
- "message": "Name is required",
907
- "code": "too_small"
908
- }
909
- ],
910
- "timestamp": "2024-01-19T10:30:00.000Z"
911
- }
912
- ```
913
-
914
- ## Creating New Endpoints
915
-
916
- 1. Create controller in `src/modules/<feature>/controller.js`:
917
-
918
- ```javascript
919
- import { z } from "zod";
920
- import { sendSuccess } from "../../utils/response.js";
921
-
922
- export const myHandlerSchema = z.object({
923
- body: z.object({
924
- name: z.string().min(1),
925
- }),
926
- });
927
-
928
- export const myHandler = (req, res) => {
929
- const { name } = req.validatedData.body;
930
- sendSuccess(res, { name }, 200, "Success");
931
- };
932
- ```
933
-
934
- 2. Add route in `src/routes.js`:
935
-
936
- ```javascript
937
- import { myHandler, myHandlerSchema } from "./modules/feature/controller.js";
938
-
939
- router.post("/feature", validateRequest(myHandlerSchema), myHandler);
940
- ```
941
-
942
- ## Error Handling
943
-
944
- The app includes comprehensive error handling:
945
-
946
- - **Zod Validation Errors** - Automatically formatted with field-level errors
947
- - **Custom Errors** - Use `AppError` for application-specific errors
948
- - **Unhandled Rejections** - Caught and logged, then process exits
949
- - **Uncaught Exceptions** - Caught and logged, then process exits
950
-
951
- ## Logging
952
-
953
- Use the logger throughout your code:
954
-
955
- ```javascript
956
- import { logger } from "./utils/logger.js";
957
-
958
- logger.debug("Debug message", { data: true });
959
- logger.info("Info message", { data: true });
960
- logger.warn("Warning message", { data: true });
961
- logger.error("Error message", { data: true });
962
- ```
963
-
964
- ## Production Checklist
965
-
966
- - [ ] Set `NODE_ENV=production`
967
- - [ ] Configure `CORS_ORIGIN` for your domain
968
- - [ ] Set appropriate `LOG_LEVEL`
969
- - [ ] Add database connection
970
- - [ ] Implement authentication middleware
971
- - [ ] Add rate limiting
972
- - [ ] Add input sanitization
973
- - [ ] Set up monitoring
974
- - [ ] Configure reverse proxy (nginx/apache)
975
-
976
- ## License
977
-
978
- ISC
1
+ # Getting Started with Charcole API
2
+
3
+ Welcome! This guide will help you set up and start using the Charcole API framework.
4
+
5
+ ## 📚 Table of Contents
6
+
7
+ 1. [Installation](#installation)
8
+ 2. [Project Structure](#project-structure)
9
+ 3. [Configuration](#configuration)
10
+ 4. [Creating Your First Endpoint](#creating-your-first-endpoint)
11
+ 5. [API Documentation with Swagger](#api-documentation-with-swagger)
12
+ 6. [Error Handling](#error-handling)
13
+ 7. [Validation](#validation)
14
+ 8. [Logging](#logging)
15
+ 9. [Running Your API](#running-your-api)
16
+ 10. [Troubleshooting](#troubleshooting)
17
+
18
+ ---
19
+
20
+ ## 🔧 Installation
21
+
22
+ ### Prerequisites
23
+
24
+ - Node.js 18+ ([Download](https://nodejs.org/))
25
+ - npm or yarn
26
+
27
+ ### Setup Steps
28
+
29
+ 1. **Create Charcole App**
30
+
31
+ ```bash
32
+ npx create-charcole@latest charcole-demo
33
+ ```
34
+
35
+ 2. **Create environment file**
36
+
37
+ The `create-charcole` CLI will automatically create a `.env` from `.env.example` and initialize a Git repository for you. Edit the generated `.env` as needed.
38
+
39
+ If you prefer to create it manually:
40
+
41
+ ```bash
42
+ cp .env.example .env
43
+ ```
44
+
45
+ 3. **Run the charcole**
46
+ ```bash
47
+ npm run dev
48
+ ```
49
+
50
+ You should see:
51
+
52
+ ```
53
+ [2024-01-20T12:00:00.000Z] INFO: Express app configured successfully
54
+ [2024-01-20T12:00:00.000Z] INFO: 🔥 Server running in development mode
55
+ ```
56
+
57
+ ---
58
+
59
+ ## 📂 Project Structure
60
+
61
+ Understanding the folder structure:
62
+
63
+ ```
64
+ charcole-demo/
65
+ ├── src/ # All application code
66
+ ├── config/
67
+ │ │ ├── env.js # Environment validation
68
+ │ │# Validates all .env variables at startup
69
+ │ │ │ # If invalid, server won't start
70
+ │ │
71
+ │ │ └── constants.js # HTTP status codes & error messages
72
+ │ │ # Use these in your code
73
+ │ │
74
+ ├── middlewares/ # Express middleware
75
+ │ │ ├── errorHandler.js # IMPORTANT: Global error handler
76
+ │ │ │ # Every error flows through here
77
+ │ │ │ # Also exports: asyncHandler, AppError classes
78
+ │ │ │
79
+ │ │ ├── validateRequest.js # Validates request body, query, params
80
+ │ │# Uses Zod schemas
81
+ │ │ │ # Throws ValidationError if invalid
82
+ │ │
83
+ │ │ └── requestLogger.js # Logs all incoming requests
84
+ │ │ # Logs: method, path, status, duration, IP
85
+ │ │
86
+ ├── modules/ # Feature modules (organized by feature)
87
+ │ │ └── health/
88
+ │ │ ├── controller.js # Route handlers for this feature
89
+ │ │ # Export: getHealth, createItem, etc.
90
+ │ │ │ # Export: validation schemas
91
+ │ │
92
+ │ │ └── service.js # (Optional) Business logic
93
+ │ │ └── model.js # (Optional) Data models
94
+ │ │
95
+ ├── utils/
96
+ │ │ ├── AppError.js # IMPORTANT: Error class hierarchy
97
+ │ │ │ # Use these to throw errors
98
+ │ │ │ # ValidationError, NotFoundError, etc.
99
+ │ │
100
+ │ │ ├── logger.js # Structured logging
101
+ │ │ │ # Use: logger.info(), logger.error(), etc.
102
+ │ │
103
+ │ │ └── response.js # Response helpers
104
+ │ │ # Use: sendSuccess() for responses
105
+ │ │
106
+ │ ├── app.js # Express app configuration
107
+ │ │ # All middleware setup
108
+ │ │ # Error handler registered here (last)
109
+ │ │
110
+ ├── routes.js # All API routes
111
+ # Import handlers from modules
112
+ # Define routes here
113
+
114
+ │ └── server.js # Server entry point
115
+ # Listen on PORT
116
+ # Graceful shutdown handling
117
+
118
+ ├── .env # Environment variables (GITIGNORED)
119
+ ├── .env.example # Example env variables (committed)
120
+ ├── package.json # Dependencies & scripts
121
+ ├── README.md # Project overview (for GitHub/npm)
122
+ └── template/README.md # This file - Getting started guide
123
+ ```
124
+
125
+ ### Creating New Modules
126
+
127
+ Create a new feature:
128
+
129
+ ```bash
130
+ # Create directory
131
+ mkdir -p src/modules/users
132
+
133
+ # Create files
134
+ touch src/modules/users/controller.js
135
+ touch src/modules/users/service.js
136
+ ```
137
+
138
+ **controller.js** - Route handlers
139
+
140
+ ```javascript
141
+ import { asyncHandler } from "../../middlewares/errorHandler.js";
142
+ import { sendSuccess } from "../../utils/response.js";
143
+ import { z } from "zod";
144
+
145
+ export const getUserSchema = z.object({
146
+ params: z.object({ id: z.string() }),
147
+ });
148
+
149
+ export const getUser = asyncHandler(async (req, res) => {
150
+ const { id } = req.params;
151
+ const user = await findUserById(id);
152
+ if (!user) throw new NotFoundError("User");
153
+ sendSuccess(res, user);
154
+ });
155
+ ```
156
+
157
+ ---
158
+
159
+ ## ⚙️ Configuration
160
+
161
+ ### Environment Variables
162
+
163
+ Edit `.env`:
164
+
165
+ ```env
166
+ # Server
167
+ NODE_ENV=development # development, production, or test
168
+ PORT=3000 # Server port
169
+
170
+ # Logging
171
+ LOG_LEVEL=info # debug, info, warn, error
172
+ # In production, use warn or error
173
+
174
+ # CORS
175
+ CORS_ORIGIN=* # Change to your domain in production
176
+ # Example: https://myapp.com
177
+
178
+ # Timeouts
179
+ REQUEST_TIMEOUT=30000 # 30 seconds
180
+ ```
181
+
182
+ ### Using Environment Variables
183
+
184
+ ```javascript
185
+ import { env } from "./config/env.js";
186
+
187
+ console.log(env.PORT); // 3000
188
+ console.log(env.NODE_ENV); // development
189
+ console.log(env.isProduction); // false
190
+ console.log(env.isDevelopment); // true
191
+ ```
192
+
193
+ ---
194
+
195
+ ## 🚀 Creating Your First Endpoint
196
+
197
+ ### Step 1: Create Controller
198
+
199
+ **src/modules/posts/controller.js**
200
+
201
+ ```javascript
202
+ import { z } from "zod";
203
+ import { asyncHandler } from "../../middlewares/errorHandler.js";
204
+ import { validateRequest } from "../../middlewares/validateRequest.js";
205
+ import { sendSuccess } from "../../utils/response.js";
206
+ import { NotFoundError } from "../../middlewares/errorHandler.js";
207
+
208
+ // Define validation schema
209
+ export const createPostSchema = z.object({
210
+ body: z.object({
211
+ title: z.string().min(1, "Title required").max(200),
212
+ content: z.string().min(1, "Content required"),
213
+ }),
214
+ });
215
+
216
+ // Define handler
217
+ export const createPost = asyncHandler(async (req, res) => {
218
+ const { title, content } = req.validatedData.body;
219
+
220
+ // Your logic here
221
+ const post = {
222
+ id: "1",
223
+ title,
224
+ content,
225
+ createdAt: new Date(),
226
+ };
227
+
228
+ sendSuccess(res, post, 201, "Post created successfully");
229
+ });
230
+
231
+ export const getPost = asyncHandler(async (req, res) => {
232
+ const { id } = req.params;
233
+
234
+ // Simulate database fetch
235
+ if (id !== "1") {
236
+ throw new NotFoundError("Post", { id });
237
+ }
238
+
239
+ const post = { id: "1", title: "Hello", content: "World" };
240
+ sendSuccess(res, post);
241
+ });
242
+ ```
243
+
244
+ ### Step 2: Register Routes
245
+
246
+ **src/routes.js**
247
+
248
+ ```javascript
249
+ import { Router } from "express";
250
+ import { getHealth, createItem } from "./modules/health/controller.js";
251
+ import {
252
+ createPost,
253
+ getPost,
254
+ createPostSchema,
255
+ } from "./modules/posts/controller.js";
256
+ import { validateRequest } from "./middlewares/validateRequest.js";
257
+
258
+ const router = Router();
259
+
260
+ // Health check
261
+ router.get("/health", getHealth);
262
+
263
+ // Posts
264
+ router.post("/posts", validateRequest(createPostSchema), createPost);
265
+ router.get("/posts/:id", getPost);
266
+
267
+ export default router;
268
+ ```
269
+
270
+ ### Step 3: Test Your Endpoint
271
+
272
+ ```bash
273
+ # Start server
274
+ npm run dev
275
+
276
+ # Test creation
277
+ curl -X POST http://localhost:3000/api/posts \
278
+ -H "Content-Type: application/json" \
279
+ -d '{"title":"My Post","content":"Hello World"}'
280
+
281
+ # Test retrieval
282
+ curl http://localhost:3000/api/posts/1
283
+
284
+ # Test 404
285
+ curl http://localhost:3000/api/posts/999
286
+ ```
287
+
288
+ ---
289
+
290
+ ## 🛡️ Error Handling
291
+
292
+ ### Understanding Errors
293
+
294
+ There are **two types of errors**:
295
+
296
+ #### 1. Operational Errors (Expected)
297
+
298
+ User/input errors that can be handled gracefully.
299
+
300
+ ```javascript
301
+ import {
302
+ ValidationError, // 422 - Input validation failed
303
+ BadRequestError, // 400 - Malformed request
304
+ AuthenticationError, // 401 - Invalid credentials
305
+ AuthorizationError, // 403 - Permission denied
306
+ NotFoundError, // 404 - Resource not found
307
+ ConflictError, // 409 - Duplicate/conflict
308
+ AppError, // Generic error
309
+ } from "./middlewares/errorHandler.js";
310
+
311
+ // Throw operational errors
312
+ throw new NotFoundError("User", { id: userId });
313
+ throw new ConflictError("Email already exists");
314
+ throw new AuthenticationError("Invalid password");
315
+ ```
316
+
317
+ #### 2. Programmer Errors (Bugs)
318
+
319
+ Unexpected errors that indicate code issues.
320
+
321
+ ```javascript
322
+ // These are automatically caught and handled:
323
+ // - TypeError
324
+ // - ReferenceError
325
+ // - SyntaxError
326
+ // - Any unhandled error
327
+
328
+ // Example: This is caught automatically
329
+ const user = null;
330
+ user.name; // TypeError: Cannot read property 'name' of null
331
+ // Logged as ERROR with stack trace
332
+ // → Generic response sent to client
333
+ ```
334
+
335
+ ### Using asyncHandler
336
+
337
+ **ALWAYS wrap async handlers** to catch errors:
338
+
339
+ ```javascript
340
+ // CORRECT - Error is caught
341
+ router.get(
342
+ "/users/:id",
343
+ asyncHandler(async (req, res) => {
344
+ const user = await User.findById(req.params.id); // Error caught
345
+ if (!user) throw new NotFoundError("User");
346
+ sendSuccess(res, user);
347
+ }),
348
+ );
349
+
350
+ // ❌ WRONG - Error NOT caught!
351
+ router.get("/users/:id", async (req, res) => {
352
+ const user = await User.findById(req.params.id); // Error leaks!
353
+ sendSuccess(res, user);
354
+ });
355
+ ```
356
+
357
+ ### Response Format
358
+
359
+ **Success (200):**
360
+
361
+ ```json
362
+ {
363
+ "success": true,
364
+ "message": "User created successfully",
365
+ "data": { "id": "123", "name": "John" },
366
+ "timestamp": "2024-01-20T12:00:00.000Z"
367
+ }
368
+ ```
369
+
370
+ **Validation Error (422):**
371
+
372
+ ```json
373
+ {
374
+ "success": false,
375
+ "message": "Validation failed",
376
+ "code": "VALIDATION_ERROR",
377
+ "statusCode": 422,
378
+ "errors": [
379
+ {
380
+ "field": "email",
381
+ "message": "Invalid email address",
382
+ "code": "invalid_email"
383
+ }
384
+ ],
385
+ "timestamp": "2024-01-20T12:00:00.000Z"
386
+ }
387
+ ```
388
+
389
+ **Not Found (404):**
390
+
391
+ ```json
392
+ {
393
+ "success": false,
394
+ "message": "User not found",
395
+ "code": "NOT_FOUND",
396
+ "statusCode": 404,
397
+ "context": { "id": "999" },
398
+ "timestamp": "2024-01-20T12:00:00.000Z"
399
+ }
400
+ ```
401
+
402
+ ---
403
+
404
+ ## 📖 API Documentation with Swagger
405
+
406
+ Your API comes with **automatic interactive documentation** powered by Swagger UI!
407
+
408
+ ### Accessing the Documentation
409
+
410
+ 1. Start your server: `npm run dev`
411
+ 2. Visit: **http://localhost:3000/api-docs**
412
+
413
+ You'll see all your APIs automatically documented and can test them directly from the browser!
414
+
415
+ ### How It Works
416
+
417
+ All built-in APIs are already documented. When you create new endpoints, simply add JSDoc comments with `@swagger` annotations:
418
+
419
+ ```javascript
420
+ /**
421
+ * @swagger
422
+ * /api/users:
423
+ * get:
424
+ * summary: Get all users
425
+ * tags:
426
+ * - Users
427
+ * responses:
428
+ * 200:
429
+ * description: Success
430
+ */
431
+ export const getAllUsers = asyncHandler(async (req, res) => {
432
+ // Your code here
433
+ });
434
+ ```
435
+
436
+ That's it! Your new endpoint will automatically appear in Swagger UI.
437
+
438
+ ### Quick Example: Documenting a POST Endpoint
439
+
440
+ ```javascript
441
+ /**
442
+ * @swagger
443
+ * /api/posts:
444
+ * post:
445
+ * summary: Create a new post
446
+ * tags:
447
+ * - Posts
448
+ * requestBody:
449
+ * required: true
450
+ * content:
451
+ * application/json:
452
+ * schema:
453
+ * type: object
454
+ * required:
455
+ * - title
456
+ * properties:
457
+ * title:
458
+ * type: string
459
+ * example: My First Post
460
+ * content:
461
+ * type: string
462
+ * example: This is the post content
463
+ * responses:
464
+ * 201:
465
+ * description: Post created successfully
466
+ * 400:
467
+ * description: Validation error
468
+ */
469
+ export const createPost = asyncHandler(async (req, res) => {
470
+ // Your implementation
471
+ });
472
+ ```
473
+
474
+ ### Protected Endpoints (with Authentication)
475
+
476
+ For endpoints that require authentication, add the `security` field:
477
+
478
+ ```javascript
479
+ /**
480
+ * @swagger
481
+ * /api/profile:
482
+ * get:
483
+ * summary: Get user profile
484
+ * tags:
485
+ * - Profile
486
+ * security:
487
+ * - bearerAuth: []
488
+ * responses:
489
+ * 200:
490
+ * description: Profile retrieved
491
+ * 401:
492
+ * description: Unauthorized
493
+ */
494
+ router.get("/profile", requireAuth, getProfile);
495
+ ```
496
+
497
+ ### 📘 Complete Guide
498
+
499
+ For comprehensive examples including:
500
+
501
+ - Path and query parameters
502
+ - File uploads
503
+ - Complex schemas
504
+ - Error responses
505
+ - CRUD operations
506
+
507
+ See the **[Complete Swagger Documentation Guide](src/lib/swagger/SWAGGER_GUIDE.md)**
508
+
509
+ ### Testing APIs in Swagger UI
510
+
511
+ 1. Open http://localhost:3000/api-docs
512
+ 2. Click on any endpoint to expand it
513
+ 3. Click "Try it out"
514
+ 4. Fill in the parameters
515
+ 5. Click "Execute"
516
+ 6. See the response!
517
+
518
+ For protected endpoints:
519
+
520
+ 1. Click the "Authorize" button at the top
521
+ 2. Enter your JWT token
522
+ 3. Now you can test protected endpoints
523
+
524
+ ---
525
+
526
+ ## ✔️ Validation
527
+
528
+ ### Zod Schema Basics
529
+
530
+ ```javascript
531
+ import { z } from "zod";
532
+
533
+ // Define schema
534
+ const userSchema = z.object({
535
+ body: z.object({
536
+ email: z.string().email("Invalid email"),
537
+ name: z.string().min(1, "Name required"),
538
+ age: z.number().min(18, "Must be 18+").optional(),
539
+ }),
540
+ query: z.object({
541
+ page: z.coerce.number().default(1),
542
+ }),
543
+ });
544
+
545
+ // Use in route
546
+ router.post("/users", validateRequest(userSchema), handler);
547
+ ```
548
+
549
+ ### Common Validations
550
+
551
+ ```javascript
552
+ // String
553
+ z.string().min(1, "Required");
554
+ z.string().email("Invalid email");
555
+ z.string().url("Invalid URL");
556
+ z.string().regex(/^\d+$/, "Numbers only");
557
+
558
+ // Number
559
+ z.number().min(0).max(100);
560
+ z.coerce.number(); // Convert string to number
561
+
562
+ // Array
563
+ z.array(z.string());
564
+ z.array(z.object({ id: z.string() }));
565
+
566
+ // Object
567
+ z.object({ key: z.string() });
568
+
569
+ // Union
570
+ z.union([z.string(), z.number()]);
571
+
572
+ // Optional
573
+ z.string().optional();
574
+ z.string().default("value");
575
+
576
+ // Enum
577
+ z.enum(["active", "inactive"]);
578
+ ```
579
+
580
+ ---
581
+
582
+ ## 📝 Logging
583
+
584
+ ### Using Logger
585
+
586
+ ```javascript
587
+ import { logger } from "./utils/logger.js";
588
+
589
+ // Different log levels
590
+ logger.debug("Detailed debug info", { data: true });
591
+ logger.info("Important information", { userId: 123 });
592
+ logger.warn("Warning - something unexpected", { statusCode: 404 });
593
+ logger.error("Error occurred", { error: "message" }, stack);
594
+ ```
595
+
596
+ ### Log Output
597
+
598
+ **Development** (colorized):
599
+
600
+ ```
601
+ [2024-01-20T12:00:00.000Z] DEBUG: Debug message
602
+ [2024-01-20T12:00:00.000Z] INFO: Request processed
603
+ [2024-01-20T12:00:00.000Z] WARN: User not found
604
+ [2024-01-20T12:00:00.000Z] ERROR: Database error
605
+ ```
606
+
607
+ **Production** (to file/service):
608
+
609
+ ```
610
+ {"level":"info","message":"Request processed","timestamp":"2024-01-20T12:00:00.000Z"}
611
+ {"level":"error","message":"Database error","timestamp":"2024-01-20T12:00:00.000Z"}
612
+ ```
613
+
614
+ ---
615
+
616
+ ## 🎯 Running Your API
617
+
618
+ ### Development
619
+
620
+ ```bash
621
+ # Start with auto-reload
622
+ npm run dev
623
+
624
+ # Output:
625
+ # [2024-01-20T12:00:00.000Z] INFO: Express app configured successfully
626
+ # [2024-01-20T12:00:00.000Z] INFO: 🔥 Server running in development mode
627
+ # { "url": "http://localhost:3000", "port": 3000 }
628
+ ```
629
+
630
+ ### Production
631
+
632
+ ```bash
633
+ # Set environment
634
+ export NODE_ENV=production
635
+
636
+ # Start server
637
+ npm start
638
+ ```
639
+
640
+ ### Testing
641
+
642
+ ```bash
643
+ # Test API endpoints
644
+ node test-api.js
645
+ ```
646
+
647
+ ---
648
+
649
+ ## 🆘 Troubleshooting
650
+
651
+ ### Server won't start
652
+
653
+ **Error:** `listen EADDRINUSE: address already in use :::3000`
654
+
655
+ **Solution:**
656
+
657
+ ```bash
658
+ # Kill process on port 3000
659
+ # macOS/Linux:
660
+ lsof -ti:3000 | xargs kill -9
661
+
662
+ # Windows PowerShell:
663
+ Get-Process | Where-Object {$_.Port -eq 3000} | Stop-Process -Force
664
+ ```
665
+
666
+ ### Validation errors not working
667
+
668
+ **Ensure you:**
669
+
670
+ 1. Import `validateRequest` from middlewares
671
+ 2. Add it as middleware before handler
672
+ 3. Pass schema with body/query/params structure
673
+ 4. Use `req.validatedData` in handler
674
+
675
+ ```javascript
676
+ // Correct
677
+ const schema = z.object({
678
+ body: z.object({ name: z.string() }),
679
+ });
680
+ router.post("/items", validateRequest(schema), handler);
681
+
682
+ // In handler:
683
+ const { name } = req.validatedData.body;
684
+ ```
685
+
686
+ ### Errors not being caught
687
+
688
+ **Ensure you:**
689
+
690
+ 1. Wrap handler with `asyncHandler`
691
+ 2. Throw AppError instances
692
+ 3. Don't try-catch to return res.status()
693
+
694
+ ```javascript
695
+ // Correct
696
+ router.get(
697
+ "/items/:id",
698
+ asyncHandler(async (req, res) => {
699
+ const item = await Item.findById(req.params.id);
700
+ if (!item) throw new NotFoundError("Item");
701
+ sendSuccess(res, item);
702
+ }),
703
+ );
704
+ ```
705
+
706
+ ### Environment variables not loading
707
+
708
+ **Check:**
709
+
710
+ 1. `.env` file exists in root
711
+ 2. Variable names match (case-sensitive)
712
+ 3. Restart server after changing .env
713
+ 4. Use `env.VARIABLE_NAME` to access
714
+
715
+ ```javascript
716
+ import { env } from "./config/env.js";
717
+
718
+ console.log(env.PORT); // Will be validated at startup
719
+ ```
720
+
721
+ ### CORS errors
722
+
723
+ **In development**, CORS should be `*`:
724
+
725
+ ```env
726
+ CORS_ORIGIN=*
727
+ ```
728
+
729
+ **In production**, set your domain:
730
+
731
+ ```env
732
+ CORS_ORIGIN=https://myapp.com
733
+ ```
734
+
735
+ ---
736
+
737
+ ## 📖 Next Steps
738
+
739
+ 1. **Read:** [Main README](../README.md) - Project overview
740
+ 2. **Learn:** [Quick Reference](../QUICK_REFERENCE.md) - Patterns & rules
741
+ 3. **Deep Dive:** [Error Handling Guide](../ERROR_HANDLING.md) - Full documentation
742
+ 4. **Build:** Create your first module using examples above
743
+ 5. **Deploy:** Follow production checklist in README
744
+
745
+ ---
746
+
747
+ ## 🤔 Common Patterns
748
+
749
+ ### Create with Validation
750
+
751
+ ```javascript
752
+ const createSchema = z.object({
753
+ body: z.object({
754
+ name: z.string().min(1),
755
+ email: z.string().email(),
756
+ }),
757
+ });
758
+
759
+ export const create = asyncHandler(async (req, res) => {
760
+ const { name, email } = req.validatedData.body;
761
+ const item = await Item.create({ name, email });
762
+ sendSuccess(res, item, 201, "Created");
763
+ });
764
+
765
+ router.post("/items", validateRequest(createSchema), create);
766
+ ```
767
+
768
+ ### Get with 404 Handling
769
+
770
+ ```javascript
771
+ export const getById = asyncHandler(async (req, res) => {
772
+ const item = await Item.findById(req.params.id);
773
+ if (!item) throw new NotFoundError("Item");
774
+ sendSuccess(res, item);
775
+ });
776
+
777
+ router.get("/items/:id", getById);
778
+ ```
779
+
780
+ ### Update with Validation
781
+
782
+ ```javascript
783
+ const updateSchema = z.object({
784
+ params: z.object({ id: z.string() }),
785
+ body: z.object({
786
+ name: z.string().min(1).optional(),
787
+ email: z.string().email().optional(),
788
+ }),
789
+ });
790
+
791
+ export const update = asyncHandler(async (req, res) => {
792
+ const item = await Item.findByIdAndUpdate(
793
+ req.params.id,
794
+ req.validatedData.body,
795
+ );
796
+ if (!item) throw new NotFoundError("Item");
797
+ sendSuccess(res, item, 200, "Updated");
798
+ });
799
+
800
+ router.patch("/items/:id", validateRequest(updateSchema), update);
801
+ ```
802
+
803
+ ### Delete with 404 Handling
804
+
805
+ ```javascript
806
+ export const delete = asyncHandler(async (req, res) => {
807
+ const item = await Item.findByIdAndDelete(req.params.id);
808
+ if (!item) throw new NotFoundError("Item");
809
+ sendSuccess(res, { id: item.id }, 200, "Deleted");
810
+ });
811
+
812
+ router.delete("/items/:id", delete);
813
+ ```
814
+
815
+ ---
816
+
817
+ **You're ready to build!** 🚀
818
+
819
+ Questions? Check the [Full Documentation](../ERROR_HANDLING.md).
820
+ PORT=3000 # Server port
821
+ LOG_LEVEL=info # debug, info, warn, error
822
+ CORS_ORIGIN=\* # CORS origin
823
+ REQUEST_TIMEOUT=30000 # Request timeout in ms
824
+
825
+ ````
826
+
827
+ ## Running
828
+
829
+ **Development** (with auto-reload):
830
+
831
+ ```bash
832
+ npm run dev
833
+ ````
834
+
835
+ **Production**:
836
+
837
+ ```bash
838
+ npm start
839
+ ```
840
+
841
+ ## API Endpoints
842
+
843
+ ### Health Check
844
+
845
+ ```
846
+ GET /health
847
+ ```
848
+
849
+ Response:
850
+
851
+ ```json
852
+ {
853
+ "success": true,
854
+ "message": "Success",
855
+ "data": {
856
+ "status": "healthy",
857
+ "uptime": 42.123,
858
+ "timestamp": "2024-01-19T10:30:00.000Z"
859
+ },
860
+ "timestamp": "2024-01-19T10:30:00.000Z"
861
+ }
862
+ ```
863
+
864
+ ### Create Item (Example with Validation)
865
+
866
+ ```
867
+ POST /api/items
868
+ Content-Type: application/json
869
+
870
+ {
871
+ "name": "Example Item",
872
+ "description": "Optional description"
873
+ }
874
+ ```
875
+
876
+ ## Response Format
877
+
878
+ All API responses follow a consistent format:
879
+
880
+ **Success**:
881
+
882
+ ```json
883
+ {
884
+ "success": true,
885
+ "message": "Success message",
886
+ "data": {},
887
+ "timestamp": "2024-01-19T10:30:00.000Z"
888
+ }
889
+ ```
890
+
891
+ **Error**:
892
+
893
+ ```json
894
+ {
895
+ "success": false,
896
+ "message": "Error message",
897
+ "timestamp": "2024-01-19T10:30:00.000Z"
898
+ }
899
+ ```
900
+
901
+ **Validation Error**:
902
+
903
+ ```json
904
+ {
905
+ "success": false,
906
+ "message": "Validation failed",
907
+ "errors": [
908
+ {
909
+ "field": "name",
910
+ "message": "Name is required",
911
+ "code": "too_small"
912
+ }
913
+ ],
914
+ "timestamp": "2024-01-19T10:30:00.000Z"
915
+ }
916
+ ```
917
+
918
+ ## Creating New Endpoints
919
+
920
+ 1. Create controller in `src/modules/<feature>/controller.js`:
921
+
922
+ ```javascript
923
+ import { z } from "zod";
924
+ import { sendSuccess } from "../../utils/response.js";
925
+
926
+ export const myHandlerSchema = z.object({
927
+ body: z.object({
928
+ name: z.string().min(1),
929
+ }),
930
+ });
931
+
932
+ export const myHandler = (req, res) => {
933
+ const { name } = req.validatedData.body;
934
+ sendSuccess(res, { name }, 200, "Success");
935
+ };
936
+ ```
937
+
938
+ 2. Add route in `src/routes.js`:
939
+
940
+ ```javascript
941
+ import { myHandler, myHandlerSchema } from "./modules/feature/controller.js";
942
+
943
+ router.post("/feature", validateRequest(myHandlerSchema), myHandler);
944
+ ```
945
+
946
+ ## Error Handling
947
+
948
+ The app includes comprehensive error handling:
949
+
950
+ - **Zod Validation Errors** - Automatically formatted with field-level errors
951
+ - **Custom Errors** - Use `AppError` for application-specific errors
952
+ - **Unhandled Rejections** - Caught and logged, then process exits
953
+ - **Uncaught Exceptions** - Caught and logged, then process exits
954
+
955
+ ## Logging
956
+
957
+ Use the logger throughout your code:
958
+
959
+ ```javascript
960
+ import { logger } from "./utils/logger.js";
961
+
962
+ logger.debug("Debug message", { data: true });
963
+ logger.info("Info message", { data: true });
964
+ logger.warn("Warning message", { data: true });
965
+ logger.error("Error message", { data: true });
966
+ ```
967
+
968
+ ## Production Checklist
969
+
970
+ - [ ] Set `NODE_ENV=production`
971
+ - [ ] Configure `CORS_ORIGIN` for your domain
972
+ - [ ] Set appropriate `LOG_LEVEL`
973
+ - [ ] Add database connection
974
+ - [ ] Implement authentication middleware
975
+ - [ ] Add rate limiting
976
+ - [ ] Add input sanitization
977
+ - [ ] Set up monitoring
978
+ - [ ] Configure reverse proxy (nginx/apache)
979
+
980
+ ## License
981
+
982
+ ISC