create-charcole 2.2.0 → 2.2.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 (72) 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 +354 -354
  5. package/bin/index.js +444 -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/package-lock.json +1715 -1715
  10. package/packages/swagger/package.json +57 -44
  11. package/packages/swagger/src/index.d.ts +126 -126
  12. package/packages/swagger/src/index.js +12 -12
  13. package/packages/swagger/src/setup.js +100 -100
  14. package/template/js/.env.example +15 -15
  15. package/template/js/README.md +978 -978
  16. package/template/js/basePackage.json +26 -26
  17. package/template/js/src/app.js +81 -81
  18. package/template/js/src/config/constants.js +20 -20
  19. package/template/js/src/config/env.js +26 -26
  20. package/template/js/src/config/swagger.config.js +15 -15
  21. package/template/js/src/middlewares/errorHandler.js +180 -180
  22. package/template/js/src/middlewares/requestLogger.js +33 -33
  23. package/template/js/src/middlewares/validateRequest.js +42 -42
  24. package/template/js/src/modules/auth/auth.constants.js +3 -3
  25. package/template/js/src/modules/auth/auth.controller.js +29 -29
  26. package/template/js/src/modules/auth/auth.middlewares.js +19 -19
  27. package/template/js/src/modules/auth/auth.routes.js +131 -131
  28. package/template/js/src/modules/auth/auth.schemas.js +60 -60
  29. package/template/js/src/modules/auth/auth.service.js +67 -67
  30. package/template/js/src/modules/auth/package.json +6 -6
  31. package/template/js/src/modules/health/controller.js +151 -151
  32. package/template/js/src/modules/swagger/package.json +5 -5
  33. package/template/js/src/repositories/user.repo.js +19 -19
  34. package/template/js/src/routes/index.js +25 -25
  35. package/template/js/src/routes/protected.js +57 -57
  36. package/template/js/src/server.js +38 -38
  37. package/template/js/src/utils/AppError.js +182 -182
  38. package/template/js/src/utils/logger.js +73 -73
  39. package/template/js/src/utils/response.js +51 -51
  40. package/template/ts/.env.example +15 -15
  41. package/template/ts/README.md +978 -978
  42. package/template/ts/basePackage.json +36 -36
  43. package/template/ts/build.js +46 -46
  44. package/template/ts/src/app.ts +71 -71
  45. package/template/ts/src/config/constants.ts +27 -27
  46. package/template/ts/src/config/env.ts +40 -40
  47. package/template/ts/src/config/swagger.config.ts +30 -30
  48. package/template/ts/src/middlewares/errorHandler.ts +201 -201
  49. package/template/ts/src/middlewares/requestLogger.ts +38 -38
  50. package/template/ts/src/middlewares/validateRequest.ts +46 -46
  51. package/template/ts/src/modules/auth/auth.constants.ts +6 -6
  52. package/template/ts/src/modules/auth/auth.controller.ts +32 -32
  53. package/template/ts/src/modules/auth/auth.middlewares.ts +46 -46
  54. package/template/ts/src/modules/auth/auth.routes.ts +52 -52
  55. package/template/ts/src/modules/auth/auth.schemas.ts +73 -73
  56. package/template/ts/src/modules/auth/auth.service.ts +106 -106
  57. package/template/ts/src/modules/auth/package.json +10 -10
  58. package/template/ts/src/modules/health/controller.ts +80 -80
  59. package/template/ts/src/modules/swagger/package.json +5 -5
  60. package/template/ts/src/repositories/user.repo.ts +33 -33
  61. package/template/ts/src/routes/index.ts +24 -24
  62. package/template/ts/src/routes/protected.ts +46 -46
  63. package/template/ts/src/server.ts +41 -41
  64. package/template/ts/src/types/express.d.ts +9 -9
  65. package/template/ts/src/utils/AppError.ts +220 -220
  66. package/template/ts/src/utils/logger.ts +55 -55
  67. package/template/ts/src/utils/response.ts +100 -100
  68. package/template/ts/tsconfig.json +26 -26
  69. package/tmpclaude-1049-cwd +0 -1
  70. package/tmpclaude-3e37-cwd +0 -1
  71. package/tmpclaude-4d73-cwd +0 -1
  72. package/tmpclaude-8a8e-cwd +0 -1
@@ -1,978 +1,978 @@
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
+ ```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