create-charcole 1.0.0

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