create-charcole 2.0.4 → 2.1.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +200 -14
  2. package/README.md +137 -332
  3. package/bin/index.js +236 -55
  4. package/bin/lib/pkgManager.js +8 -25
  5. package/bin/lib/templateHandler.js +5 -42
  6. package/package.json +2 -2
  7. package/plans/V2_1_PLAN.md +20 -0
  8. package/template/js/.env.example +8 -0
  9. package/template/js/basePackage.json +11 -13
  10. package/template/js/src/app.js +4 -1
  11. package/template/js/src/modules/auth/auth.constants.js +3 -0
  12. package/template/js/src/modules/auth/auth.controller.js +29 -0
  13. package/template/js/src/modules/auth/auth.middlewares.js +19 -0
  14. package/template/js/src/modules/auth/auth.routes.js +9 -0
  15. package/template/js/src/modules/auth/auth.schemas.js +60 -0
  16. package/template/js/src/modules/auth/auth.service.js +67 -0
  17. package/template/js/src/modules/auth/package.json +6 -0
  18. package/template/js/src/repositories/user.repo.js +19 -0
  19. package/template/js/src/routes/index.js +25 -0
  20. package/template/js/src/routes/protected.js +13 -0
  21. package/template/ts/.env.example +8 -0
  22. package/template/ts/basePackage.json +19 -15
  23. package/template/ts/build.js +46 -0
  24. package/template/ts/src/app.ts +5 -4
  25. package/template/ts/src/middlewares/errorHandler.ts +15 -23
  26. package/template/ts/src/middlewares/requestLogger.ts +1 -1
  27. package/template/ts/src/middlewares/validateRequest.ts +1 -1
  28. package/template/ts/src/modules/auth/auth.constants.ts +6 -0
  29. package/template/ts/src/modules/auth/auth.controller.ts +32 -0
  30. package/template/ts/src/modules/auth/auth.middlewares.ts +46 -0
  31. package/template/ts/src/modules/auth/auth.routes.ts +9 -0
  32. package/template/ts/src/modules/auth/auth.schemas.ts +73 -0
  33. package/template/ts/src/modules/auth/auth.service.ts +106 -0
  34. package/template/ts/src/modules/auth/package.json +10 -0
  35. package/template/ts/src/modules/health/controller.ts +3 -3
  36. package/template/ts/src/repositories/user.repo.ts +33 -0
  37. package/template/ts/src/routes/index.ts +24 -0
  38. package/template/ts/src/routes/protected.ts +13 -0
  39. package/template/ts/src/server.ts +0 -1
  40. package/template/ts/src/utils/logger.ts +1 -1
  41. package/template/ts/tsconfig.json +14 -7
  42. package/template/js/ARCHITECTURE_DIAGRAMS.md +0 -283
  43. package/template/js/CHECKLIST.md +0 -279
  44. package/template/js/COMPLETE.md +0 -405
  45. package/template/js/ERROR_HANDLING.md +0 -393
  46. package/template/js/IMPLEMENTATION.md +0 -368
  47. package/template/js/IMPLEMENTATION_COMPLETE.md +0 -363
  48. package/template/js/INDEX.md +0 -290
  49. package/template/js/QUICK_REFERENCE.md +0 -270
  50. package/template/js/package.json +0 -28
  51. package/template/js/src/routes.js +0 -17
  52. package/template/js/test-api.js +0 -100
  53. package/template/ts/ARCHITECTURE_DIAGRAMS.md +0 -283
  54. package/template/ts/CHECKLIST.md +0 -279
  55. package/template/ts/COMPLETE.md +0 -405
  56. package/template/ts/ERROR_HANDLING.md +0 -393
  57. package/template/ts/IMPLEMENTATION.md +0 -368
  58. package/template/ts/IMPLEMENTATION_COMPLETE.md +0 -363
  59. package/template/ts/INDEX.md +0 -290
  60. package/template/ts/QUICK_REFERENCE.md +0 -270
  61. package/template/ts/package.json +0 -32
  62. package/template/ts/src/app.js +0 -75
  63. package/template/ts/src/config/constants.js +0 -20
  64. package/template/ts/src/config/env.js +0 -26
  65. package/template/ts/src/middlewares/errorHandler.js +0 -180
  66. package/template/ts/src/middlewares/requestLogger.js +0 -33
  67. package/template/ts/src/middlewares/validateRequest.js +0 -42
  68. package/template/ts/src/modules/health/controller.js +0 -50
  69. package/template/ts/src/routes.js +0 -17
  70. package/template/ts/src/routes.ts +0 -16
  71. package/template/ts/src/server.js +0 -38
  72. package/template/ts/src/utils/AppError.js +0 -182
  73. package/template/ts/src/utils/logger.js +0 -73
  74. package/template/ts/src/utils/response.js +0 -51
  75. package/template/ts/test-api.js +0 -100
@@ -1,270 +0,0 @@
1
- # Production-Level Error Handling System - Quick Reference
2
-
3
- ## 🎯 Problem Solved
4
-
5
- ❌ **Before:** Random `res.status(500).json(...)` scattered throughout code
6
- ✅ **After:** Single centralized error handler with distinguished error types
7
-
8
- ## 🏗️ System Architecture
9
-
10
- ```
11
- Every Request
12
-
13
- Route Handler (wrapped with asyncHandler)
14
-
15
- ├─→ Success? → sendSuccess(res, data)
16
-
17
- └─→ Error thrown ✘
18
-
19
- Global Error Handler
20
-
21
- Normalize Error
22
- ├─→ Is it AppError? Use it
23
- ├─→ Is it ZodError? Convert to ValidationError
24
- ├─→ Is it TypeError/ReferenceError/etc? Convert to InternalServerError
25
-
26
- Classify Error
27
- ├─→ Operational? (isOperational: true)
28
- │ ├─ Log as WARN with context
29
- │ └─ Send full details to client
30
-
31
- └─→ Programmer Error? (isOperational: false)
32
- ├─ Log as ERROR with stack trace
33
- └─ Send generic message in production
34
-
35
- Send Consistent JSON Response
36
- ```
37
-
38
- ## 📦 Error Classes (Use These!)
39
-
40
- ```javascript
41
- import {
42
- AppError, // Base class
43
- ValidationError, // Input validation failed
44
- AuthenticationError, // Auth failed (401)
45
- AuthorizationError, // Permission denied (403)
46
- NotFoundError, // Resource not found (404)
47
- ConflictError, // Duplicate/conflict (409)
48
- BadRequestError, // Malformed request (400)
49
- InternalServerError, // Unexpected error (500)
50
- } from "./middlewares/errorHandler.js";
51
- ```
52
-
53
- ## 🎬 Quick Start
54
-
55
- ### 1. Throw Operational Errors
56
-
57
- ```javascript
58
- // Validation
59
- throw new ValidationError("Invalid input", [
60
- { field: "email", message: "Invalid email", code: "invalid_email" },
61
- ]);
62
-
63
- // Not found
64
- throw new NotFoundError("User", { id: userId });
65
-
66
- // Duplicate
67
- throw new ConflictError("Email already exists", { email });
68
-
69
- // Auth
70
- throw new AuthenticationError("Invalid credentials");
71
-
72
- // Permission
73
- throw new AuthorizationError("Access denied");
74
-
75
- // Generic operational error
76
- throw new AppError("Request failed", 400, {
77
- isOperational: true,
78
- code: "CUSTOM_ERROR",
79
- context: { details: "..." },
80
- });
81
- ```
82
-
83
- ### 2. Wrap All Async Handlers
84
-
85
- ```javascript
86
- import { asyncHandler } from "./middlewares/errorHandler.js";
87
-
88
- // ✅ Correct
89
- router.post(
90
- "/users",
91
- asyncHandler(async (req, res) => {
92
- const user = await User.create(req.body);
93
- sendSuccess(res, user, 201);
94
- }),
95
- );
96
-
97
- // ❌ Wrong - errors leak!
98
- router.post("/users", async (req, res) => {
99
- const user = await User.create(req.body); // Error not caught!
100
- sendSuccess(res, user, 201);
101
- });
102
- ```
103
-
104
- ### 3. Use Validation Middleware
105
-
106
- ```javascript
107
- import { validateRequest } from "./middlewares/validateRequest.js";
108
- import { z } from "zod";
109
-
110
- const createUserSchema = z.object({
111
- body: z.object({
112
- email: z.string().email("Invalid email"),
113
- name: z.string().min(1, "Name required"),
114
- }),
115
- });
116
-
117
- router.post(
118
- "/users",
119
- validateRequest(createUserSchema),
120
- asyncHandler(async (req, res) => {
121
- // req.validatedData.body has been validated
122
- const user = await User.create(req.validatedData.body);
123
- sendSuccess(res, user, 201, "User created");
124
- }),
125
- );
126
- ```
127
-
128
- ## 📊 Logging Behavior
129
-
130
- | Error Type | Level | Includes Stack | Info Sent to Client |
131
- | ---------------------- | ----- | -------------- | ---------------------- |
132
- | ValidationError | WARN | ❌ | ✅ All details |
133
- | NotFoundError | WARN | ❌ | ✅ All details |
134
- | AuthenticationError | WARN | ❌ | ✅ All details |
135
- | TypeError (programmer) | ERROR | ✅ | ❌ Generic only (prod) |
136
- | Unhandled (programmer) | FATAL | ✅ | ❌ Generic only (prod) |
137
-
138
- ## 📋 Response Format
139
-
140
- All responses are consistent JSON:
141
-
142
- ### Success
143
-
144
- ```json
145
- {
146
- "success": true,
147
- "message": "User created successfully",
148
- "data": { "id": "123", "name": "John" },
149
- "timestamp": "2024-01-19T15:55:30.000Z"
150
- }
151
- ```
152
-
153
- ### Operational Error
154
-
155
- ```json
156
- {
157
- "success": false,
158
- "message": "User not found",
159
- "code": "NOT_FOUND",
160
- "statusCode": 404,
161
- "context": { "id": "999" },
162
- "timestamp": "2024-01-19T15:55:30.000Z"
163
- }
164
- ```
165
-
166
- ### Validation Error
167
-
168
- ```json
169
- {
170
- "success": false,
171
- "message": "Validation failed",
172
- "code": "VALIDATION_ERROR",
173
- "statusCode": 422,
174
- "errors": [
175
- { "field": "email", "message": "Invalid email", "code": "invalid_email" }
176
- ],
177
- "timestamp": "2024-01-19T15:55:30.000Z"
178
- }
179
- ```
180
-
181
- ### Programmer Error (Production)
182
-
183
- ```json
184
- {
185
- "success": false,
186
- "message": "Internal server error",
187
- "code": "INTERNAL_SERVER_ERROR",
188
- "timestamp": "2024-01-19T15:55:30.000Z"
189
- }
190
- ```
191
-
192
- ## 🛡️ Golden Rules
193
-
194
- 1. **Always use asyncHandler** for async route handlers
195
-
196
- ```javascript
197
- router.get("/endpoint", asyncHandler(async (req, res) => { ... }))
198
- ```
199
-
200
- 2. **Never use res.status().json()** for errors - throw AppError instead
201
-
202
- ```javascript
203
- // ❌ Wrong
204
- res.status(400).json({ error: "..." });
205
-
206
- // ✅ Correct
207
- throw new BadRequestError("Invalid input");
208
- ```
209
-
210
- 3. **Validate request early** with validateRequest middleware
211
-
212
- ```javascript
213
- router.post("/endpoint", validateRequest(schema), handler);
214
- ```
215
-
216
- 4. **Throw, don't catch** - let global handler catch
217
-
218
- ```javascript
219
- // ❌ Wrong
220
- try {
221
- await someTask();
222
- } catch (err) {
223
- res.status(500).json(err);
224
- }
225
-
226
- // ✅ Correct
227
- await someTask(); // Error bubbles up to global handler
228
- ```
229
-
230
- 5. **Include context** when throwing errors
231
- ```javascript
232
- throw new NotFoundError("User", { id: userId, email: userEmail });
233
- ```
234
-
235
- ## 🚀 Features at a Glance
236
-
237
- | Feature | Status |
238
- | ------------------------------------------------ | ------ |
239
- | Centralized error handler | ✅ |
240
- | Error classification (operational vs programmer) | ✅ |
241
- | Validation error formatting | ✅ |
242
- | Async error wrapping | ✅ |
243
- | Stack trace logging | ✅ |
244
- | Production sanitization | ✅ |
245
- | Request logging | ✅ |
246
- | Graceful shutdown | ✅ |
247
- | Unhandled exception catching | ✅ |
248
- | Consistent JSON responses | ✅ |
249
-
250
- ## 📚 Full Documentation
251
-
252
- - **[ERROR_HANDLING.md](ERROR_HANDLING.md)** - Comprehensive guide with examples
253
- - **[IMPLEMENTATION_COMPLETE.md](IMPLEMENTATION_COMPLETE.md)** - Full implementation details
254
-
255
- ## 🎓 Key Files
256
-
257
- | File | Purpose |
258
- | ------------------------------------ | ----------------------------------- |
259
- | `src/utils/AppError.js` | Error class hierarchy |
260
- | `src/middlewares/errorHandler.js` | Global error handler + asyncHandler |
261
- | `src/middlewares/validateRequest.js` | Zod validation middleware |
262
- | `src/middlewares/requestLogger.js` | Request logging |
263
- | `src/utils/logger.js` | Structured logging with colors |
264
- | `src/utils/response.js` | Success response helpers |
265
-
266
- ---
267
-
268
- **That's it! Your API now has enterprise-grade error handling.** 🎯
269
-
270
- Every error flows through one place. Every response is consistent. Errors are properly classified and logged. You're ready for production. 🚀
@@ -1,32 +0,0 @@
1
- {
2
- "name": "charcole",
3
- "version": "2.0.4",
4
- "description": "Production-grade Node.js Express API",
5
- "main": "src/server.js",
6
- "scripts": {
7
- "start": "node src/server.js",
8
- "dev": "nodemon src/server.js",
9
- "lint": "echo 'Add linting here'",
10
- "test": "echo 'Add tests here'"
11
- },
12
- "engines": {
13
- "node": ">=18.0.0"
14
- },
15
- "keywords": [],
16
- "author": "",
17
- "license": "ISC",
18
- "type": "module",
19
- "dependencies": {
20
- "cors": "^2.8.5",
21
- "dotenv": "^16.3.1",
22
- "express": "^4.18.2",
23
- "zod": "^3.22.4"
24
- },
25
- "devDependencies": {
26
- "@types/express": "^4.17.21",
27
- "@types/node": "^25.0.10",
28
- "nodemon": "^3.0.2",
29
- "ts-node-dev": "^2.0.0",
30
- "typescript": "^5.9.3"
31
- }
32
- }
@@ -1,75 +0,0 @@
1
- import express from "express";
2
- import cors from "cors";
3
- import { env } from "./config/env.js";
4
- import { HTTP_STATUS, ERROR_MESSAGES } from "./config/constants.js";
5
- import { requestLogger } from "./middlewares/requestLogger.js";
6
- import {
7
- errorHandler,
8
- asyncHandler,
9
- NotFoundError,
10
- } from "./middlewares/errorHandler.js";
11
- import { sendSuccess } from "./utils/response.js";
12
- import { logger } from "./utils/logger.js";
13
- import routes from "./routes.js";
14
-
15
- export const app = express();
16
-
17
- // Trust proxy
18
- app.set("trust proxy", 1);
19
-
20
- // CORS Configuration
21
- app.use(
22
- cors({
23
- origin: env.CORS_ORIGIN,
24
- credentials: true,
25
- methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
26
- allowedHeaders: ["Content-Type", "Authorization"],
27
- }),
28
- );
29
-
30
- // Body parsing middleware
31
- app.use(express.json({ limit: "10mb" }));
32
- app.use(express.urlencoded({ extended: true, limit: "10mb" }));
33
-
34
- // Request timeout
35
- app.use((req, res, next) => {
36
- req.setTimeout(env.REQUEST_TIMEOUT);
37
- res.setTimeout(env.REQUEST_TIMEOUT);
38
- next();
39
- });
40
-
41
- // Request logging
42
- app.use(requestLogger);
43
-
44
- // API Routes
45
- app.use("/api", routes);
46
-
47
- // Root health endpoint
48
- app.get(
49
- "/",
50
- asyncHandler(async (req, res) => {
51
- sendSuccess(
52
- res,
53
- {
54
- message: "Welcome to Charcole API",
55
- version: "1.0.0",
56
- environment: env.NODE_ENV,
57
- },
58
- 200,
59
- "API is online",
60
- );
61
- }),
62
- );
63
-
64
- // 404 handler
65
- app.use((req, res, next) => {
66
- throw new NotFoundError(`${req.method} ${req.path}`, {
67
- method: req.method,
68
- path: req.path,
69
- });
70
- });
71
-
72
- // Global error handler (MUST be last)
73
- app.use(errorHandler);
74
-
75
- logger.info("Express app configured successfully");
@@ -1,20 +0,0 @@
1
- export const HTTP_STATUS = {
2
- OK: 200,
3
- CREATED: 201,
4
- BAD_REQUEST: 400,
5
- UNAUTHORIZED: 401,
6
- FORBIDDEN: 403,
7
- NOT_FOUND: 404,
8
- CONFLICT: 409,
9
- UNPROCESSABLE_ENTITY: 422,
10
- INTERNAL_SERVER_ERROR: 500,
11
- SERVICE_UNAVAILABLE: 503,
12
- };
13
-
14
- export const ERROR_MESSAGES = {
15
- VALIDATION_ERROR: "Validation failed",
16
- NOT_FOUND: "Resource not found",
17
- UNAUTHORIZED: "Unauthorized",
18
- SERVER_ERROR: "Internal server error",
19
- BAD_REQUEST: "Bad request",
20
- };
@@ -1,26 +0,0 @@
1
- import { z } from "zod";
2
-
3
- const envSchema = z.object({
4
- NODE_ENV: z
5
- .enum(["development", "production", "test"])
6
- .default("development"),
7
- PORT: z.coerce.number().default(3000),
8
- LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
9
- CORS_ORIGIN: z.string().default("*"),
10
- REQUEST_TIMEOUT: z.coerce.number().default(30000),
11
- });
12
-
13
- const parseEnv = () => {
14
- try {
15
- return envSchema.parse(process.env);
16
- } catch (error) {
17
- console.error("❌ Invalid environment variables:", error.errors);
18
- process.exit(1);
19
- }
20
- };
21
-
22
- export const env = parseEnv();
23
-
24
- export const isDevelopment = env.NODE_ENV === "development";
25
- export const isProduction = env.NODE_ENV === "production";
26
- export const isTest = env.NODE_ENV === "test";
@@ -1,180 +0,0 @@
1
- import { ZodError } from "zod";
2
- import { HTTP_STATUS, ERROR_MESSAGES } from "../config/constants.js";
3
- import { logger } from "../utils/logger.js";
4
- import {
5
- AppError,
6
- ValidationError,
7
- InternalServerError,
8
- } from "../utils/AppError.js";
9
- import { env } from "../config/env.js";
10
-
11
- /**
12
- * Normalize different error types to AppError
13
- */
14
- const normalizeError = (err) => {
15
- // Already an AppError
16
- if (err instanceof AppError) {
17
- return err;
18
- }
19
-
20
- // Zod validation error
21
- if (err instanceof ZodError) {
22
- const errors = err.errors.map((e) => ({
23
- field: e.path.join("."),
24
- message: e.message,
25
- code: e.code,
26
- }));
27
- return new ValidationError(ERROR_MESSAGES.VALIDATION_ERROR, errors);
28
- }
29
-
30
- // Syntax errors (programmer error)
31
- if (err instanceof SyntaxError) {
32
- return new InternalServerError("Syntax error in application code", err, {
33
- type: "SyntaxError",
34
- });
35
- }
36
-
37
- // Type errors (programmer error)
38
- if (err instanceof TypeError) {
39
- return new InternalServerError("Type error in application", err, {
40
- type: "TypeError",
41
- });
42
- }
43
-
44
- // Reference errors (programmer error)
45
- if (err instanceof ReferenceError) {
46
- return new InternalServerError("Reference error in application", err, {
47
- type: "ReferenceError",
48
- });
49
- }
50
-
51
- // Range errors
52
- if (err instanceof RangeError) {
53
- return new InternalServerError("Range error in application", err, {
54
- type: "RangeError",
55
- });
56
- }
57
-
58
- // Generic error (unknown)
59
- return new InternalServerError(
60
- err.message || ERROR_MESSAGES.SERVER_ERROR,
61
- err,
62
- { type: "UnknownError" },
63
- );
64
- };
65
-
66
- /**
67
- * Log error based on type (operational vs programmer)
68
- */
69
- const logError = (appError, req) => {
70
- const errorDetails = {
71
- type: appError.isOperational ? "OPERATIONAL" : "PROGRAMMER",
72
- code: appError.code,
73
- message: appError.message,
74
- statusCode: appError.statusCode,
75
- method: req.method,
76
- path: req.path,
77
- query: req.query,
78
- ip: req.ip,
79
- userAgent: req.get("user-agent"),
80
- };
81
-
82
- // Operational errors: normal logging
83
- if (appError.isOperational) {
84
- logger.warn(`Operational Error: ${appError.code}`, errorDetails);
85
- } else {
86
- // Programmer errors: detailed logging with stack
87
- logger.error(
88
- `Programmer Error: ${appError.code}`,
89
- errorDetails,
90
- appError.stack,
91
- );
92
- }
93
-
94
- // Log validation errors separately
95
- if (appError instanceof ValidationError && appError.errors) {
96
- logger.debug("Validation errors", { errors: appError.errors });
97
- }
98
-
99
- // Log cause if present
100
- if (appError.cause) {
101
- logger.debug("Error cause", { cause: appError.cause.message });
102
- }
103
- };
104
-
105
- /**
106
- * Send error response
107
- */
108
- const sendErrorResponse = (res, appError) => {
109
- const statusCode = appError.statusCode || HTTP_STATUS.INTERNAL_SERVER_ERROR;
110
-
111
- // In production, hide internal details for programmer errors
112
- if (!appError.isOperational && env.isProduction) {
113
- return res.status(statusCode).json({
114
- success: false,
115
- message: ERROR_MESSAGES.SERVER_ERROR,
116
- code: "INTERNAL_SERVER_ERROR",
117
- timestamp: new Date().toISOString(),
118
- });
119
- }
120
-
121
- // Return detailed error in development
122
- const response = appError.toJSON
123
- ? appError.toJSON()
124
- : {
125
- success: false,
126
- message: appError.message,
127
- code: appError.code,
128
- statusCode,
129
- timestamp: new Date().toISOString(),
130
- };
131
-
132
- return res.status(statusCode).json(response);
133
- };
134
-
135
- /**
136
- * Global Error Handler Middleware
137
- *
138
- * This middleware catches all errors and provides a unified way to handle them.
139
- * MUST be the last middleware in the app.
140
- *
141
- * Features:
142
- * - Distinguishes between operational and programmer errors
143
- * - Logs errors with full context
144
- * - Hides sensitive info in production
145
- * - Formats JSON responses consistently
146
- */
147
- export const errorHandler = (err, req, res, next) => {
148
- // Normalize the error
149
- const appError = normalizeError(err);
150
-
151
- // Log the error
152
- logError(appError, req);
153
-
154
- // Send response
155
- sendErrorResponse(res, appError);
156
- };
157
-
158
- /**
159
- * Async error wrapper
160
- * Wrap async route handlers to catch errors and pass to middleware
161
- *
162
- * Example:
163
- * router.get('/users/:id', asyncHandler(getUserHandler))
164
- */
165
- export const asyncHandler = (fn) => {
166
- return (req, res, next) => {
167
- return Promise.resolve(fn(req, res, next)).catch(next);
168
- };
169
- };
170
-
171
- export {
172
- AppError,
173
- ValidationError,
174
- InternalServerError,
175
- AuthenticationError,
176
- AuthorizationError,
177
- NotFoundError,
178
- ConflictError,
179
- BadRequestError,
180
- } from "../utils/AppError.js";
@@ -1,33 +0,0 @@
1
- import { logger } from "../utils/logger.js";
2
-
3
- /**
4
- * Request logging middleware
5
- * Logs all HTTP requests with method, path, status, duration, and IP
6
- */
7
- export const requestLogger = (req, res, next) => {
8
- const start = Date.now();
9
-
10
- res.on("finish", () => {
11
- const duration = Date.now() - start;
12
- const statusCode = res.statusCode;
13
- const isError = statusCode >= 400;
14
-
15
- const logData = {
16
- method: req.method,
17
- path: req.path,
18
- statusCode,
19
- durationMs: duration,
20
- ip: req.ip,
21
- userAgent: req.get("user-agent"),
22
- ...(isError && { error: true }),
23
- };
24
-
25
- if (isError) {
26
- logger.warn(`${req.method} ${req.path}`, logData);
27
- } else {
28
- logger.debug(`${req.method} ${req.path}`, logData);
29
- }
30
- });
31
-
32
- next();
33
- };
@@ -1,42 +0,0 @@
1
- import { ValidationError } from "../utils/AppError.js";
2
-
3
- /**
4
- * Request validation middleware
5
- *
6
- * Validates request body, query, and params against a Zod schema
7
- * Throws ValidationError if validation fails
8
- *
9
- * Example:
10
- * const schema = z.object({
11
- * body: z.object({ name: z.string() }),
12
- * query: z.object({ page: z.coerce.number().optional() }),
13
- * });
14
- *
15
- * router.post('/items', validateRequest(schema), handler)
16
- */
17
- export const validateRequest = (schema) => {
18
- return async (req, res, next) => {
19
- try {
20
- // Validate request
21
- const validated = await schema.parseAsync({
22
- body: req.body,
23
- query: req.query,
24
- params: req.params,
25
- });
26
-
27
- // Attach validated data
28
- req.validatedData = validated;
29
- next();
30
- } catch (error) {
31
- if (error.name === "ZodError") {
32
- const errors = error.errors.map((e) => ({
33
- field: e.path.join("."),
34
- message: e.message,
35
- code: e.code,
36
- }));
37
- throw new ValidationError("Request validation failed", errors);
38
- }
39
- next(error);
40
- }
41
- };
42
- };