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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sheraz Manzoor
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,398 @@
1
+ # Charcole API
2
+
3
+ > **Production-grade Node.js Express API with enterprise-level error handling, Zod validation, and structured logging.**
4
+
5
+ [![Node.js](https://img.shields.io/badge/Node.js-18+-green.svg)](https://nodejs.org/)
6
+ [![Express.js](https://img.shields.io/badge/Express-4.18+-blue.svg)](https://expressjs.com/)
7
+ [![Zod](https://img.shields.io/badge/Zod-3.22+-purple.svg)](https://zod.dev/)
8
+ [![License: ISC](https://img.shields.io/badge/License-ISC-yellow.svg)](LICENSE)
9
+
10
+ ## 🎯 What This Is
11
+
12
+ A **production-ready Node.js Express backend** with:
13
+
14
+ - ✅ **Centralized Error Handling** - Every error flows through one place
15
+ - ✅ **Error Classification** - Operational vs Programmer errors distinguished
16
+ - ✅ **Zod Validation** - Type-safe schema validation with automatic error formatting
17
+ - ✅ **Structured Logging** - Color-coded logs with context and stack traces
18
+ - ✅ **Consistent JSON Responses** - Standardized format across all endpoints
19
+ - ✅ **Production-Safe** - Internal details hidden from clients in production
20
+ - ✅ **Async Error Handling** - Promise rejection leaks prevented with asyncHandler
21
+ - ✅ **Graceful Shutdown** - Proper cleanup on SIGTERM/SIGINT
22
+ - ✅ **Request Logging** - Method, path, status, duration, IP automatically tracked
23
+ - ✅ **Unhandled Exception Catching** - All edge cases caught and logged
24
+
25
+ ## 🚀 Quick Start
26
+
27
+ ### Installation
28
+
29
+ ```bash
30
+ # Create your charcole app now
31
+ npx create-charcole@latest charcole-demo
32
+
33
+ # Configure environment
34
+ cp .env.example .env
35
+
36
+ # Start development server (with auto-reload)
37
+ npm run dev
38
+
39
+ # OR start production server
40
+ npm start
41
+ ```
42
+
43
+ Server runs on `http://localhost:3000` by default.
44
+
45
+ ## 📋 Key Features
46
+
47
+ ### 🛡️ Enterprise-Grade Error Handling
48
+
49
+ **No More `res.status(500).json(...)`**
50
+
51
+ Every error in your application flows through a centralized global error handler that:
52
+
53
+ 1. **Normalizes** all error types (ZodError, TypeError, custom AppError, etc.)
54
+ 2. **Classifies** errors as operational (expected) or programmer (bugs)
55
+ 3. **Logs** appropriately (WARN for operational, ERROR with stack for programmer)
56
+ 4. **Sanitizes** responses (hides details in production, shows context in dev)
57
+
58
+ ```javascript
59
+ // ✅ Throw AppError - ALWAYS
60
+ throw new NotFoundError("User", { id: userId });
61
+ throw new ValidationError("Invalid input", errors);
62
+ throw new ConflictError("Email already exists");
63
+
64
+ // ❌ Never do this
65
+ res.status(404).json({ error: "Not found" });
66
+ ```
67
+
68
+ ### 🔐 Type-Safe Validation
69
+
70
+ ```javascript
71
+ import { z } from "zod";
72
+ import { validateRequest } from "./middlewares/validateRequest.js";
73
+
74
+ const createUserSchema = z.object({
75
+ body: z.object({
76
+ email: z.string().email(),
77
+ name: z.string().min(1),
78
+ }),
79
+ });
80
+
81
+ router.post("/users", validateRequest(createUserSchema), handler);
82
+ ```
83
+
84
+ ### 📝 Structured Logging
85
+
86
+ ```javascript
87
+ import { logger } from "./utils/logger.js";
88
+
89
+ logger.debug("Debug message", { data: true });
90
+ logger.info("Info message", { data: true });
91
+ logger.warn("Warning message", { data: true });
92
+ logger.error("Error message", { data: true });
93
+ ```
94
+
95
+ ### 📊 Consistent JSON Responses
96
+
97
+ All responses follow the same format:
98
+
99
+ **Success:**
100
+
101
+ ```json
102
+ {
103
+ "success": true,
104
+ "message": "User created successfully",
105
+ "data": { "id": "123", "name": "John" },
106
+ "timestamp": "2024-01-20T12:00:00.000Z"
107
+ }
108
+ ```
109
+
110
+ **Error:**
111
+
112
+ ```json
113
+ {
114
+ "success": false,
115
+ "message": "User not found",
116
+ "code": "NOT_FOUND",
117
+ "statusCode": 404,
118
+ "context": { "id": "999" },
119
+ "timestamp": "2024-01-20T12:00:00.000Z"
120
+ }
121
+ ```
122
+
123
+ **Validation Error:**
124
+
125
+ ```json
126
+ {
127
+ "success": false,
128
+ "message": "Validation failed",
129
+ "code": "VALIDATION_ERROR",
130
+ "statusCode": 422,
131
+ "errors": [
132
+ { "field": "email", "message": "Invalid email", "code": "invalid_email" }
133
+ ],
134
+ "timestamp": "2024-01-20T12:00:00.000Z"
135
+ }
136
+ ```
137
+
138
+ ## 🏗️ Error Classes
139
+
140
+ Use these specialized error classes:
141
+
142
+ ```javascript
143
+ import {
144
+ AppError, // Base class
145
+ ValidationError, // 422 - Input validation failed
146
+ BadRequestError, // 400 - Malformed request
147
+ AuthenticationError, // 401 - Invalid credentials
148
+ AuthorizationError, // 403 - Permission denied
149
+ NotFoundError, // 404 - Resource not found
150
+ ConflictError, // 409 - Duplicate/conflict
151
+ InternalServerError, // 500 - Unexpected error
152
+ } from "./middlewares/errorHandler.js";
153
+ ```
154
+
155
+ ## 📚 Documentation
156
+
157
+ | Document | Purpose |
158
+ | ------------------------------------------------- | --------------------------------- |
159
+ | [Getting Started](template/README.md) | Setup & directory structure guide |
160
+ | [Quick Reference](QUICK_REFERENCE.md) | Quick patterns & golden rules |
161
+ | [Error Handling Guide](ERROR_HANDLING.md) | Comprehensive error documentation |
162
+ | [Architecture Diagrams](ARCHITECTURE_DIAGRAMS.md) | Visual system architecture |
163
+ | [Full Implementation](IMPLEMENTATION_COMPLETE.md) | Complete implementation details |
164
+
165
+ ## 🎓 4 Golden Rules
166
+
167
+ 1. **Wrap async handlers** with `asyncHandler`
168
+
169
+ ```javascript
170
+ router.get("/users/:id", asyncHandler(async (req, res) => { ... }))
171
+ ```
172
+
173
+ 2. **Throw AppError** (never use `res.status().json()`)
174
+
175
+ ```javascript
176
+ throw new NotFoundError("User", { id });
177
+ ```
178
+
179
+ 3. **Validate requests** with `validateRequest`
180
+
181
+ ```javascript
182
+ router.post("/users", validateRequest(schema), handler);
183
+ ```
184
+
185
+ 4. **Send success** with `sendSuccess`
186
+ ```javascript
187
+ sendSuccess(res, data, 201, "User created");
188
+ ```
189
+
190
+ ## 📂 Project Structure
191
+
192
+ ```
193
+ src/
194
+ ├── config/
195
+ │ ├── env.js # Environment validation with Zod
196
+ │ └── constants.js # HTTP status codes & error messages
197
+ ├── middlewares/
198
+ │ ├── errorHandler.js # ⭐ Global error handler + asyncHandler
199
+ │ ├── validateRequest.js # Request validation middleware
200
+ │ └── requestLogger.js # Request logging
201
+ ├── modules/
202
+ │ └── health/
203
+ │ └── controller.js # Example handlers
204
+ ├── utils/
205
+ │ ├── AppError.js # ⭐ Error class hierarchy
206
+ │ ├── logger.js # Structured logging
207
+ │ └── response.js # Success response helpers
208
+ ├── app.js # Express app setup
209
+ ├── routes.js # API routes
210
+ └── server.js # Server entry point
211
+ ```
212
+
213
+ ## 🚀 Running
214
+
215
+ ```bash
216
+ # Development (with auto-reload and full logging)
217
+ npm run dev
218
+
219
+ # Production (optimized, minimal logging)
220
+ npm start
221
+
222
+ # Test API endpoints
223
+ node test-api.js
224
+ ```
225
+
226
+ ## 🔧 Configuration
227
+
228
+ Environment variables (see `.env.example`):
229
+
230
+ ```env
231
+ NODE_ENV=development # development, production, test
232
+ PORT=3000 # Server port
233
+ LOG_LEVEL=info # debug, info, warn, error
234
+ CORS_ORIGIN=* # CORS allowed origins
235
+ REQUEST_TIMEOUT=30000 # Request timeout in milliseconds
236
+ ```
237
+
238
+ ## 💻 Example: Create User Endpoint
239
+
240
+ ```javascript
241
+ import { asyncHandler, ConflictError } from "./middlewares/errorHandler.js";
242
+ import { validateRequest } from "./middlewares/validateRequest.js";
243
+ import { sendSuccess } from "./utils/response.js";
244
+ import { z } from "zod";
245
+
246
+ // 1. Define validation schema
247
+ const createUserSchema = z.object({
248
+ body: z.object({
249
+ email: z.string().email("Invalid email"),
250
+ name: z.string().min(1, "Name required").max(100),
251
+ }),
252
+ });
253
+
254
+ // 2. Define handler (wrapped with asyncHandler)
255
+ export const createUser = asyncHandler(async (req, res) => {
256
+ const { email, name } = req.validatedData.body;
257
+
258
+ // Check for duplicate
259
+ const exists = await User.findOne({ email });
260
+ if (exists) {
261
+ throw new ConflictError("Email already exists", { email });
262
+ }
263
+
264
+ // Create user (any error is automatically caught)
265
+ const user = await User.create({ email, name });
266
+
267
+ // Send success
268
+ sendSuccess(res, user, 201, "User created successfully");
269
+ });
270
+
271
+ // 3. Use in routes
272
+ router.post("/users", validateRequest(createUserSchema), createUser);
273
+ ```
274
+
275
+ **Results:**
276
+
277
+ - ✅ Valid request → 201 with user data
278
+ - ✅ Invalid email → 422 with field errors
279
+ - ✅ Duplicate email → 409 conflict
280
+ - ✅ Database error → 500 (logged, generic message sent in prod)
281
+
282
+ ## 🌐 API Endpoints
283
+
284
+ All endpoints follow the same error handling pattern:
285
+
286
+ ```
287
+ GET / # Root - API info
288
+ GET /api/health # Health check
289
+ POST /api/items # Create item (example)
290
+ ```
291
+
292
+ ## ✨ What Makes This Special
293
+
294
+ Unlike typical Express APIs, Charcole:
295
+
296
+ - ✅ **Distinguishes operational from programmer errors** - Different handling for expected vs unexpected errors
297
+ - ✅ **Never leaks internal details** - Production-safe error responses
298
+ - ✅ **Catches all async errors** - No promise rejections leak
299
+ - ✅ **Logs with full context** - Debugging is easy
300
+ - ✅ **Validates everything** - Zod integration prevents bad data
301
+ - ✅ **Consistent responses** - Predictable format for every endpoint
302
+ - ✅ **Production-ready** - Graceful shutdown, signal handling, etc.
303
+
304
+ ## 🔄 Error Flow
305
+
306
+ ```
307
+ Request arrives
308
+
309
+ Handler (wrapped with asyncHandler)
310
+ ├─ Success → sendSuccess() → Response sent ✓
311
+ └─ Error thrown ✘
312
+
313
+ Global error handler catches it
314
+
315
+ Error normalized & classified
316
+
317
+ Logged (WARN for operational, ERROR with stack for programmer)
318
+
319
+ Consistent JSON response sent
320
+ ```
321
+
322
+ ## 📊 Logging Examples
323
+
324
+ ### Operational Error (Expected)
325
+
326
+ ```
327
+ [2024-01-20T12:00:00.000Z] WARN: Operational Error: NOT_FOUND
328
+ { "code": "NOT_FOUND", "message": "User not found", "statusCode": 404 }
329
+ ```
330
+
331
+ ### Programmer Error (Bug)
332
+
333
+ ```
334
+ [2024-01-20T12:00:00.000Z] ERROR: Programmer Error: REFERENCE_ERROR
335
+ { "code": "REFERENCE_ERROR", "message": "user is not defined" }
336
+ ReferenceError: user is not defined
337
+ at handler.js:15:3
338
+ ...
339
+ ```
340
+
341
+ ## 🛠️ Development
342
+
343
+ ```bash
344
+ # Install dependencies
345
+ npm install
346
+
347
+ # Start dev server with auto-reload
348
+ npm run dev
349
+
350
+ # Check for syntax errors
351
+ npm run lint
352
+
353
+ # Run tests
354
+ npm test
355
+ ```
356
+
357
+ ## 📦 Dependencies
358
+
359
+ - **Express** - Web framework
360
+ - **Zod** - Schema validation
361
+ - **CORS** - Cross-origin requests
362
+ - **dotenv** - Environment variables
363
+ - **nodemon** - Auto-reload (dev only)
364
+
365
+ ## 🚢 Production Checklist
366
+
367
+ Before deploying:
368
+
369
+ - [ ] Set `NODE_ENV=production`
370
+ - [ ] Configure `CORS_ORIGIN` for your domain
371
+ - [ ] Set `LOG_LEVEL=warn` or higher
372
+ - [ ] Add database connection
373
+ - [ ] Implement authentication
374
+ - [ ] Add rate limiting
375
+ - [ ] Set up error monitoring (e.g., Sentry)
376
+ - [ ] Configure reverse proxy (nginx/apache)
377
+ - [ ] Test all error scenarios
378
+ - [ ] Verify no secrets in error responses
379
+
380
+ ## 🤝 Contributing
381
+
382
+ Contributions welcome! Please:
383
+
384
+ 1. Follow the error handling patterns
385
+ 2. Always use `asyncHandler` for async handlers
386
+ 3. Throw `AppError` instances for errors
387
+ 4. Include context in errors
388
+ 5. Add tests for new features
389
+
390
+ ## 📄 License
391
+
392
+ ISC
393
+
394
+ ---
395
+
396
+ **Made for teams that care about code quality and production reliability.** 🚀
397
+
398
+ Need help? See the [Getting Started Guide](template/README.md) or [Full Documentation](ERROR_HANDLING.md).
package/bin/index.js ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const projectName = process.argv[2];
7
+
8
+ if (!projectName) {
9
+ console.error("❌ Please provide a project name.");
10
+ console.error("Usage: npx create-charcole my-backend");
11
+ process.exit(1);
12
+ }
13
+
14
+ const currentDir = process.cwd();
15
+ const targetDir = path.join(currentDir, projectName);
16
+ const templateDir = path.join(__dirname, "..", "template");
17
+
18
+ if (fs.existsSync(targetDir)) {
19
+ console.error(`❌ Folder "${projectName}" already exists.`);
20
+ process.exit(1);
21
+ }
22
+
23
+ function copyDir(src, dest) {
24
+ fs.mkdirSync(dest, { recursive: true });
25
+
26
+ for (const file of fs.readdirSync(src)) {
27
+ const srcPath = path.join(src, file);
28
+ const destPath = path.join(dest, file);
29
+
30
+ if (fs.statSync(srcPath).isDirectory()) {
31
+ copyDir(srcPath, destPath);
32
+ } else {
33
+ fs.copyFileSync(srcPath, destPath);
34
+ }
35
+ }
36
+ }
37
+
38
+ try {
39
+ console.log("🔥 Creating Charcole app...");
40
+
41
+ copyDir(templateDir, targetDir);
42
+
43
+ const pkgPath = path.join(targetDir, "package.json");
44
+ if (fs.existsSync(pkgPath)) {
45
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
46
+ pkg.name = projectName;
47
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
48
+ }
49
+
50
+ console.log("✅ Charcole app created successfully!");
51
+ console.log("");
52
+ console.log("Next steps:");
53
+ console.log(` cd ${projectName}`);
54
+ console.log(" npm install");
55
+ console.log(" npm run dev");
56
+ console.log("");
57
+ console.log("🧱 Built with Charcole — Express, but engineered.");
58
+ } catch (err) {
59
+ console.error("❌ Failed to create Charcole app.");
60
+ console.error(err.message);
61
+ process.exit(1);
62
+ }
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "create-charcole",
3
+ "version": "1.0.0",
4
+ "description": "Production-ready Express backend starter with engineering guardrails.",
5
+ "license": "MIT",
6
+ "author": {
7
+ "name": "Sheraz Manzoor",
8
+ "email": "sheraz.dev121@gmail.com"
9
+ },
10
+ "bin": {
11
+ "create-charcole": "bin/index.js"
12
+ },
13
+ "keywords": [
14
+ "express",
15
+ "backend",
16
+ "starter",
17
+ "boilerplate",
18
+ "production",
19
+ "charcole"
20
+ ],
21
+ "engines": {
22
+ "node": ">=16"
23
+ }
24
+ }
@@ -0,0 +1,8 @@
1
+ NODE_ENV=development
2
+ PORT=3000
3
+
4
+ LOG_LEVEL=info
5
+
6
+ CORS_ORIGIN=*
7
+
8
+ REQUEST_TIMEOUT=30000