create-node-advance-app 2.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/bin/cli.js +138 -0
- package/lib/templates/index.js +2138 -0
- package/package.json +49 -0
|
@@ -0,0 +1,2138 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
// Helper to write files
|
|
5
|
+
function writeFile(filePath, content) {
|
|
6
|
+
const dir = path.dirname(filePath);
|
|
7
|
+
if (!fs.existsSync(dir)) {
|
|
8
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
9
|
+
}
|
|
10
|
+
fs.writeFileSync(filePath, content.trim() + '\n');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function createProject(config) {
|
|
14
|
+
const {
|
|
15
|
+
projectPath,
|
|
16
|
+
projectName,
|
|
17
|
+
language,
|
|
18
|
+
database,
|
|
19
|
+
auth,
|
|
20
|
+
validation,
|
|
21
|
+
logger,
|
|
22
|
+
errorHandling,
|
|
23
|
+
docker
|
|
24
|
+
} = config;
|
|
25
|
+
|
|
26
|
+
const isTS = language === 'TypeScript';
|
|
27
|
+
const ext = isTS ? 'ts' : 'js';
|
|
28
|
+
|
|
29
|
+
// Create directories
|
|
30
|
+
const dirs = [
|
|
31
|
+
'src',
|
|
32
|
+
'src/config',
|
|
33
|
+
'src/routes',
|
|
34
|
+
'src/middlewares',
|
|
35
|
+
'src/controllers',
|
|
36
|
+
'src/services',
|
|
37
|
+
'src/utils'
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
if (isTS) dirs.push('src/types');
|
|
41
|
+
if (database !== 'none') dirs.push('src/models');
|
|
42
|
+
if (validation !== 'none') dirs.push('src/validators');
|
|
43
|
+
if (database === 'postgresql' || database === 'mysql') {
|
|
44
|
+
dirs.push('src/migrations');
|
|
45
|
+
dirs.push('src/seeders');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
dirs.forEach(dir => {
|
|
49
|
+
fs.mkdirSync(path.join(projectPath, dir), { recursive: true });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// ============================================================
|
|
53
|
+
// 1. PACKAGE.JSON
|
|
54
|
+
// ============================================================
|
|
55
|
+
const dependencies = {
|
|
56
|
+
express: '^4.18.2',
|
|
57
|
+
dotenv: '^16.3.1',
|
|
58
|
+
cors: '^2.8.5',
|
|
59
|
+
helmet: '^7.1.0'
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const devDependencies = {
|
|
63
|
+
nodemon: '^3.0.2'
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
if (isTS) {
|
|
67
|
+
devDependencies.typescript = '^5.3.3';
|
|
68
|
+
devDependencies['@types/node'] = '^20.10.6';
|
|
69
|
+
devDependencies['@types/express'] = '^4.17.21';
|
|
70
|
+
devDependencies['@types/cors'] = '^2.8.17';
|
|
71
|
+
devDependencies['ts-node'] = '^10.9.2';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Database dependencies
|
|
75
|
+
if (database === 'mongodb') {
|
|
76
|
+
dependencies.mongoose = '^8.0.3';
|
|
77
|
+
} else if (database === 'postgresql' || database === 'mysql') {
|
|
78
|
+
dependencies.sequelize = '^6.35.2';
|
|
79
|
+
devDependencies['sequelize-cli'] = '^6.6.2';
|
|
80
|
+
|
|
81
|
+
if (database === 'postgresql') {
|
|
82
|
+
dependencies.pg = '^8.11.3';
|
|
83
|
+
dependencies['pg-hstore'] = '^2.3.4';
|
|
84
|
+
} else {
|
|
85
|
+
dependencies.mysql2 = '^3.6.5';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Auth dependencies
|
|
90
|
+
if (auth) {
|
|
91
|
+
dependencies.jsonwebtoken = '^9.0.2';
|
|
92
|
+
dependencies.bcryptjs = '^2.4.3';
|
|
93
|
+
if (isTS) {
|
|
94
|
+
devDependencies['@types/jsonwebtoken'] = '^9.0.5';
|
|
95
|
+
devDependencies['@types/bcryptjs'] = '^2.4.6';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Validation dependencies
|
|
100
|
+
if (validation === 'zod') {
|
|
101
|
+
dependencies.zod = '^3.22.4';
|
|
102
|
+
} else if (validation === 'joi') {
|
|
103
|
+
dependencies.joi = '^17.11.0';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Logger dependencies
|
|
107
|
+
if (logger === 'Winston') {
|
|
108
|
+
dependencies.winston = '^3.11.0';
|
|
109
|
+
} else if (logger === 'Pino') {
|
|
110
|
+
dependencies.pino = '^8.17.2';
|
|
111
|
+
dependencies['pino-pretty'] = '^10.3.1';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const scripts = {
|
|
115
|
+
start: isTS ? 'node dist/server.js' : 'node src/server.js',
|
|
116
|
+
dev: isTS ? 'nodemon --exec ts-node src/server.ts' : 'nodemon src/server.js'
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
if (isTS) {
|
|
120
|
+
scripts.build = 'tsc';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (database === 'postgresql' || database === 'mysql') {
|
|
124
|
+
scripts['db:migrate'] = 'npx sequelize-cli db:migrate';
|
|
125
|
+
scripts['db:migrate:undo'] = 'npx sequelize-cli db:migrate:undo';
|
|
126
|
+
scripts['db:seed'] = 'npx sequelize-cli db:seed:all';
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
writeFile(path.join(projectPath, 'package.json'), JSON.stringify({
|
|
130
|
+
name: projectName,
|
|
131
|
+
version: '1.0.0',
|
|
132
|
+
description: 'Backend API',
|
|
133
|
+
main: isTS ? 'dist/server.js' : 'src/server.js',
|
|
134
|
+
scripts,
|
|
135
|
+
dependencies,
|
|
136
|
+
devDependencies
|
|
137
|
+
}, null, 2));
|
|
138
|
+
|
|
139
|
+
// ============================================================
|
|
140
|
+
// 2. ENVIRONMENT FILES
|
|
141
|
+
// ============================================================
|
|
142
|
+
let envContent = `NODE_ENV=development
|
|
143
|
+
PORT=5000
|
|
144
|
+
`;
|
|
145
|
+
|
|
146
|
+
if (database === 'mongodb') {
|
|
147
|
+
envContent += `\n# Database
|
|
148
|
+
MONGODB_URI=mongodb://localhost:27017/${projectName}
|
|
149
|
+
`;
|
|
150
|
+
} else if (database === 'postgresql') {
|
|
151
|
+
envContent += `\n# Database
|
|
152
|
+
DATABASE_URL=postgresql://postgres:root@localhost:5432/my-back
|
|
153
|
+
DB_HOST=localhost
|
|
154
|
+
DB_PORT=5432
|
|
155
|
+
DB_NAME=my-back
|
|
156
|
+
DB_USER=postgres
|
|
157
|
+
DB_PASSWORD=root
|
|
158
|
+
`;
|
|
159
|
+
} else if (database === 'mysql') {
|
|
160
|
+
envContent += `\n# Database
|
|
161
|
+
DATABASE_URL=mysql://root:password@localhost:3306/${projectName}
|
|
162
|
+
DB_HOST=localhost
|
|
163
|
+
DB_PORT=3306
|
|
164
|
+
DB_NAME=${projectName}
|
|
165
|
+
DB_USER=root
|
|
166
|
+
DB_PASSWORD=password
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (auth) {
|
|
171
|
+
envContent += `\n# JWT
|
|
172
|
+
JWT_SECRET=your-super-secret-jwt-key-change-in-production
|
|
173
|
+
JWT_EXPIRES_IN=7d
|
|
174
|
+
`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
envContent += `\n# CORS
|
|
178
|
+
CORS_ORIGIN=http://localhost:3000
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
writeFile(path.join(projectPath, '.env'), envContent);
|
|
182
|
+
writeFile(path.join(projectPath, '.env.example'), envContent);
|
|
183
|
+
|
|
184
|
+
// ============================================================
|
|
185
|
+
// 3. GITIGNORE
|
|
186
|
+
// ============================================================
|
|
187
|
+
writeFile(path.join(projectPath, '.gitignore'), `node_modules/
|
|
188
|
+
.env
|
|
189
|
+
.env.local
|
|
190
|
+
${isTS ? 'dist/\n' : ''}logs/
|
|
191
|
+
*.log
|
|
192
|
+
.DS_Store
|
|
193
|
+
coverage/
|
|
194
|
+
`);
|
|
195
|
+
|
|
196
|
+
// ============================================================
|
|
197
|
+
// 4. TYPESCRIPT CONFIG (if TypeScript)
|
|
198
|
+
// ============================================================
|
|
199
|
+
if (isTS) {
|
|
200
|
+
writeFile(path.join(projectPath, 'tsconfig.json'), JSON.stringify({
|
|
201
|
+
compilerOptions: {
|
|
202
|
+
target: 'ES2020',
|
|
203
|
+
module: 'commonjs',
|
|
204
|
+
lib: ['ES2020'],
|
|
205
|
+
outDir: './dist',
|
|
206
|
+
rootDir: './src',
|
|
207
|
+
strict: true,
|
|
208
|
+
esModuleInterop: true,
|
|
209
|
+
skipLibCheck: true,
|
|
210
|
+
forceConsistentCasingInFileNames: true,
|
|
211
|
+
resolveJsonModule: true,
|
|
212
|
+
moduleResolution: 'node',
|
|
213
|
+
types: ['node']
|
|
214
|
+
},
|
|
215
|
+
include: ['src/**/*'],
|
|
216
|
+
exclude: ['node_modules', 'dist']
|
|
217
|
+
}, null, 2));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ============================================================
|
|
221
|
+
// 5. src/config/env.ts/js - CENTRALIZED ENVIRONMENT CONSTANTS
|
|
222
|
+
// ============================================================
|
|
223
|
+
const envConfigContent = isTS ? `interface Environment {
|
|
224
|
+
NODE_ENV: string;
|
|
225
|
+
PORT: number;
|
|
226
|
+
${database === 'mongodb' ? 'MONGODB_URI: string;' : ''}
|
|
227
|
+
${database === 'postgresql' || database === 'mysql' ? `DATABASE_URL: string;
|
|
228
|
+
DB_HOST: string;
|
|
229
|
+
DB_PORT: number;
|
|
230
|
+
DB_NAME: string;
|
|
231
|
+
DB_USER: string;
|
|
232
|
+
DB_PASSWORD: string;` : ''}
|
|
233
|
+
${auth ? `JWT_SECRET: string;
|
|
234
|
+
JWT_EXPIRES_IN: string;` : ''}
|
|
235
|
+
CORS_ORIGIN: string;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export const ENV: Environment = {
|
|
239
|
+
NODE_ENV: process.env.NODE_ENV || 'development',
|
|
240
|
+
PORT: parseInt(process.env.PORT || '5000', 10),
|
|
241
|
+
${database === 'mongodb' ? "MONGODB_URI: process.env.MONGODB_URI || 'mongodb://localhost:27017/app'," : ''}
|
|
242
|
+
${database === 'postgresql' || database === 'mysql' ? `DATABASE_URL: process.env.DATABASE_URL || '',
|
|
243
|
+
DB_HOST: process.env.DB_HOST || 'localhost',
|
|
244
|
+
DB_PORT: parseInt(process.env.DB_PORT || '${database === 'postgresql' ? '5432' : '3306'}', 10),
|
|
245
|
+
DB_NAME: process.env.DB_NAME || 'my-back',
|
|
246
|
+
DB_USER: process.env.DB_USER || '${database === 'postgresql' ? 'postgres' : 'root'}',
|
|
247
|
+
DB_PASSWORD: process.env.DB_PASSWORD || 'root',` : ''}
|
|
248
|
+
${auth ? `JWT_SECRET: process.env.JWT_SECRET || 'change-this-secret',
|
|
249
|
+
JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN || '7d',` : ''}
|
|
250
|
+
CORS_ORIGIN: process.env.CORS_ORIGIN || 'http://localhost:3000'
|
|
251
|
+
};
|
|
252
|
+
` : `const ENV = {
|
|
253
|
+
NODE_ENV: process.env.NODE_ENV || 'development',
|
|
254
|
+
PORT: parseInt(process.env.PORT || '5000', 10),
|
|
255
|
+
${database === 'mongodb' ? "MONGODB_URI: process.env.MONGODB_URI || 'mongodb://localhost:27017/app'," : ''}
|
|
256
|
+
${database === 'postgresql' || database === 'mysql' ? `DATABASE_URL: process.env.DATABASE_URL || '',
|
|
257
|
+
DB_HOST: process.env.DB_HOST || 'localhost',
|
|
258
|
+
DB_PORT: parseInt(process.env.DB_PORT || '${database === 'postgresql' ? '5432' : '3306'}', 10),
|
|
259
|
+
DB_NAME: process.env.DB_NAME || 'my-back',
|
|
260
|
+
DB_USER: process.env.DB_USER || '${database === 'postgresql' ? 'postgres' : 'root'}',
|
|
261
|
+
DB_PASSWORD: process.env.DB_PASSWORD || 'root',` : ''}
|
|
262
|
+
${auth ? `JWT_SECRET: process.env.JWT_SECRET || 'change-this-secret',
|
|
263
|
+
JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN || '7d',` : ''}
|
|
264
|
+
CORS_ORIGIN: process.env.CORS_ORIGIN || 'http://localhost:3000'
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
module.exports = { ENV };
|
|
268
|
+
`;
|
|
269
|
+
|
|
270
|
+
writeFile(path.join(projectPath, `src/config/env.${ext}`), envConfigContent);
|
|
271
|
+
|
|
272
|
+
// ============================================================
|
|
273
|
+
// 6. src/utils/AppError.ts/js - CUSTOM ERROR CLASS
|
|
274
|
+
// ============================================================
|
|
275
|
+
if (errorHandling) {
|
|
276
|
+
const appErrorContent = isTS ? `export class AppError extends Error {
|
|
277
|
+
public statusCode: number;
|
|
278
|
+
public isOperational: boolean;
|
|
279
|
+
|
|
280
|
+
constructor(message: string, statusCode: number = 500) {
|
|
281
|
+
super(message);
|
|
282
|
+
this.statusCode = statusCode;
|
|
283
|
+
this.isOperational = true;
|
|
284
|
+
|
|
285
|
+
Error.captureStackTrace(this, this.constructor);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
` : `class AppError extends Error {
|
|
289
|
+
constructor(message, statusCode = 500) {
|
|
290
|
+
super(message);
|
|
291
|
+
this.statusCode = statusCode;
|
|
292
|
+
this.isOperational = true;
|
|
293
|
+
|
|
294
|
+
Error.captureStackTrace(this, this.constructor);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
module.exports = { AppError };
|
|
299
|
+
`;
|
|
300
|
+
|
|
301
|
+
writeFile(path.join(projectPath, `src/utils/AppError.${ext}`), appErrorContent);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ============================================================
|
|
305
|
+
// 7. src/utils/response.ts/js - RESPONSE HELPERS
|
|
306
|
+
// ============================================================
|
|
307
|
+
if (errorHandling) {
|
|
308
|
+
const responseContent = isTS ? `import { Response } from 'express';
|
|
309
|
+
|
|
310
|
+
export const successResponse = (
|
|
311
|
+
res: Response,
|
|
312
|
+
data: any,
|
|
313
|
+
message: string = 'Success',
|
|
314
|
+
statusCode: number = 200
|
|
315
|
+
) => {
|
|
316
|
+
return res.status(statusCode).json({
|
|
317
|
+
success: true,
|
|
318
|
+
message,
|
|
319
|
+
data
|
|
320
|
+
});
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
export const errorResponse = (
|
|
324
|
+
res: Response,
|
|
325
|
+
message: string = 'Error occurred',
|
|
326
|
+
statusCode: number = 500,
|
|
327
|
+
errors?: any
|
|
328
|
+
) => {
|
|
329
|
+
const response: any = {
|
|
330
|
+
success: false,
|
|
331
|
+
message
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
if (errors) {
|
|
335
|
+
response.errors = errors;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return res.status(statusCode).json(response);
|
|
339
|
+
};
|
|
340
|
+
` : `const successResponse = (res, data, message = 'Success', statusCode = 200) => {
|
|
341
|
+
return res.status(statusCode).json({
|
|
342
|
+
success: true,
|
|
343
|
+
message,
|
|
344
|
+
data
|
|
345
|
+
});
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const errorResponse = (res, message = 'Error occurred', statusCode = 500, errors = null) => {
|
|
349
|
+
const response = {
|
|
350
|
+
success: false,
|
|
351
|
+
message
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
if (errors) {
|
|
355
|
+
response.errors = errors;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return res.status(statusCode).json(response);
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
module.exports = { successResponse, errorResponse };
|
|
362
|
+
`;
|
|
363
|
+
|
|
364
|
+
writeFile(path.join(projectPath, `src/utils/response.${ext}`), responseContent);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// ============================================================
|
|
368
|
+
// 8. src/server.ts/js
|
|
369
|
+
// ============================================================
|
|
370
|
+
const serverContent = isTS ? `import dotenv from 'dotenv';
|
|
371
|
+
import app from './app';
|
|
372
|
+
import { ENV } from './config/env';
|
|
373
|
+
${database === 'mongodb' ? "import connectDB from './config/database';" : ''}
|
|
374
|
+
${database === 'postgresql' || database === 'mysql' ? "import { connectDatabase } from './config/database';" : ''}
|
|
375
|
+
${logger !== 'None' ? "import logger from './config/logger';" : ''}
|
|
376
|
+
|
|
377
|
+
dotenv.config();
|
|
378
|
+
|
|
379
|
+
${database === 'mongodb' ? `// Connect to MongoDB
|
|
380
|
+
connectDB();
|
|
381
|
+
` : ''}
|
|
382
|
+
${database === 'postgresql' || database === 'mysql' ? `// Connect to database
|
|
383
|
+
connectDatabase();
|
|
384
|
+
` : ''}
|
|
385
|
+
const server = app.listen(ENV.PORT, () => {
|
|
386
|
+
${logger !== 'None'
|
|
387
|
+
? "logger.info(`Server running on port ${ENV.PORT}`);"
|
|
388
|
+
: "console.log(`🚀 Server running on port ${ENV.PORT}`);"}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// Graceful shutdown
|
|
392
|
+
process.on('SIGTERM', () => {
|
|
393
|
+
${logger !== 'None' ? "logger.info('SIGTERM received, closing server...');" : "console.log('SIGTERM received');"}
|
|
394
|
+
server.close(() => {
|
|
395
|
+
${logger !== 'None' ? "logger.info('Server closed');" : "console.log('Server closed');"}
|
|
396
|
+
process.exit(0);
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
` : `require('dotenv').config();
|
|
400
|
+
const app = require('./app');
|
|
401
|
+
const { ENV } = require('./config/env');
|
|
402
|
+
${database === 'mongodb' ? "const connectDB = require('./config/database');" : ''}
|
|
403
|
+
${database === 'postgresql' || database === 'mysql' ? "const { connectDatabase } = require('./config/database');" : ''}
|
|
404
|
+
${logger !== 'None' ? "const logger = require('./config/logger');" : ''}
|
|
405
|
+
|
|
406
|
+
${database === 'mongodb' ? `// Connect to MongoDB
|
|
407
|
+
connectDB();
|
|
408
|
+
` : ''}
|
|
409
|
+
${database === 'postgresql' || database === 'mysql' ? `// Connect to database
|
|
410
|
+
connectDatabase();
|
|
411
|
+
` : ''}
|
|
412
|
+
const server = app.listen(ENV.PORT, () => {
|
|
413
|
+
${logger !== 'None'
|
|
414
|
+
? "logger.info(`Server running on port ${ENV.PORT}`);"
|
|
415
|
+
: "console.log(`🚀 Server running on port ${ENV.PORT}`);"}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Graceful shutdown
|
|
419
|
+
process.on('SIGTERM', () => {
|
|
420
|
+
${logger !== 'None' ? "logger.info('SIGTERM received, closing server...');" : "console.log('SIGTERM received');"}
|
|
421
|
+
server.close(() => {
|
|
422
|
+
${logger !== 'None' ? "logger.info('Server closed');" : "console.log('Server closed');"}
|
|
423
|
+
process.exit(0);
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
`;
|
|
427
|
+
|
|
428
|
+
writeFile(path.join(projectPath, `src/server.${ext}`), serverContent);
|
|
429
|
+
|
|
430
|
+
// ============================================================
|
|
431
|
+
// 9. src/app.ts/js
|
|
432
|
+
// ============================================================
|
|
433
|
+
const appContent = isTS ? `import express, { Application, Request, Response, NextFunction } from 'express';
|
|
434
|
+
import cors from 'cors';
|
|
435
|
+
import helmet from 'helmet';
|
|
436
|
+
import { ENV } from './config/env';
|
|
437
|
+
${errorHandling ? "import { AppError } from './utils/AppError';\nimport { errorResponse } from './utils/response';" : ''}
|
|
438
|
+
|
|
439
|
+
const app: Application = express();
|
|
440
|
+
|
|
441
|
+
// Security middleware
|
|
442
|
+
app.use(helmet());
|
|
443
|
+
app.use(cors({ origin: ENV.CORS_ORIGIN, credentials: true }));
|
|
444
|
+
|
|
445
|
+
// Body parser
|
|
446
|
+
app.use(express.json());
|
|
447
|
+
app.use(express.urlencoded({ extended: true }));
|
|
448
|
+
|
|
449
|
+
// Health check
|
|
450
|
+
app.get('/', (req: Request, res: Response) => {
|
|
451
|
+
res.json({
|
|
452
|
+
success: true,
|
|
453
|
+
message: 'Server is healthy',
|
|
454
|
+
timestamp: new Date().toISOString()
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// API Routes
|
|
459
|
+
// app.use('/api', routes);
|
|
460
|
+
|
|
461
|
+
${errorHandling ? `// 404 handler
|
|
462
|
+
app.use((req: Request, res: Response, next: NextFunction) => {
|
|
463
|
+
next(new AppError(\`Route \${req.originalUrl} not found\`, 404));
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// Global error handler
|
|
467
|
+
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
|
|
468
|
+
const statusCode = err.statusCode || 500;
|
|
469
|
+
const message = err.message || 'Internal Server Error';
|
|
470
|
+
|
|
471
|
+
if (ENV.NODE_ENV === 'development') {
|
|
472
|
+
return res.status(statusCode).json({
|
|
473
|
+
success: false,
|
|
474
|
+
message,
|
|
475
|
+
stack: err.stack,
|
|
476
|
+
error: err
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return errorResponse(res, message, statusCode);
|
|
481
|
+
});
|
|
482
|
+
` : ''}
|
|
483
|
+
export default app;
|
|
484
|
+
` : `const express = require('express');
|
|
485
|
+
const cors = require('cors');
|
|
486
|
+
const helmet = require('helmet');
|
|
487
|
+
const { ENV } = require('./config/env');
|
|
488
|
+
${errorHandling ? "const { AppError } = require('./utils/AppError');\nconst { errorResponse } = require('./utils/response');" : ''}
|
|
489
|
+
|
|
490
|
+
const app = express();
|
|
491
|
+
|
|
492
|
+
// Security middleware
|
|
493
|
+
app.use(helmet());
|
|
494
|
+
app.use(cors({ origin: ENV.CORS_ORIGIN, credentials: true }));
|
|
495
|
+
|
|
496
|
+
// Body parser
|
|
497
|
+
app.use(express.json());
|
|
498
|
+
app.use(express.urlencoded({ extended: true }));
|
|
499
|
+
|
|
500
|
+
// Health check
|
|
501
|
+
app.get('/', (req, res) => {
|
|
502
|
+
res.json({
|
|
503
|
+
success: true,
|
|
504
|
+
message: 'Server is healthy',
|
|
505
|
+
timestamp: new Date().toISOString()
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// API Routes
|
|
510
|
+
// app.use('/api', routes);
|
|
511
|
+
|
|
512
|
+
${errorHandling ? `// 404 handler
|
|
513
|
+
app.use((req, res, next) => {
|
|
514
|
+
next(new AppError(\`Route \${req.originalUrl} not found\`, 404));
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// Global error handler
|
|
518
|
+
app.use((err, req, res, next) => {
|
|
519
|
+
const statusCode = err.statusCode || 500;
|
|
520
|
+
const message = err.message || 'Internal Server Error';
|
|
521
|
+
|
|
522
|
+
if (ENV.NODE_ENV === 'development') {
|
|
523
|
+
return res.status(statusCode).json({
|
|
524
|
+
success: false,
|
|
525
|
+
message,
|
|
526
|
+
stack: err.stack,
|
|
527
|
+
error: err
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return errorResponse(res, message, statusCode);
|
|
532
|
+
});
|
|
533
|
+
` : ''}
|
|
534
|
+
module.exports = app;
|
|
535
|
+
`;
|
|
536
|
+
|
|
537
|
+
writeFile(path.join(projectPath, `src/app.${ext}`), appContent);
|
|
538
|
+
|
|
539
|
+
// ============================================================
|
|
540
|
+
// 10. DATABASE CONFIG
|
|
541
|
+
// ============================================================
|
|
542
|
+
if (database === 'mongodb') {
|
|
543
|
+
const mongoContent = isTS ? `import mongoose from 'mongoose';
|
|
544
|
+
import { ENV } from './env';
|
|
545
|
+
${logger !== 'None' ? "import logger from './logger';" : ''}
|
|
546
|
+
|
|
547
|
+
const connectDB = async (): Promise<void> => {
|
|
548
|
+
try {
|
|
549
|
+
await mongoose.connect(ENV.MONGODB_URI);
|
|
550
|
+
${logger !== 'None' ? "logger.info('MongoDB connected successfully');" : "console.log('✅ MongoDB connected');"}
|
|
551
|
+
} catch (error) {
|
|
552
|
+
${logger !== 'None' ? "logger.error('MongoDB connection error:', error);" : "console.error('❌ MongoDB error:', error);"}
|
|
553
|
+
process.exit(1);
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
export default connectDB;
|
|
558
|
+
` : `const mongoose = require('mongoose');
|
|
559
|
+
const { ENV } = require('./env');
|
|
560
|
+
${logger !== 'None' ? "const logger = require('./logger');" : ''}
|
|
561
|
+
|
|
562
|
+
const connectDB = async () => {
|
|
563
|
+
try {
|
|
564
|
+
await mongoose.connect(ENV.MONGODB_URI);
|
|
565
|
+
${logger !== 'None' ? "logger.info('MongoDB connected successfully');" : "console.log('✅ MongoDB connected');"}
|
|
566
|
+
} catch (error) {
|
|
567
|
+
${logger !== 'None' ? "logger.error('MongoDB connection error:', error);" : "console.error('❌ MongoDB error:', error);"}
|
|
568
|
+
process.exit(1);
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
module.exports = connectDB;
|
|
573
|
+
`;
|
|
574
|
+
|
|
575
|
+
writeFile(path.join(projectPath, `src/config/database.${ext}`), mongoContent);
|
|
576
|
+
|
|
577
|
+
// Info file for models folder
|
|
578
|
+
const mongooseInfoContent = isTS ? `import mongoose, { Document, Schema } from 'mongoose';
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* MODELS FOLDER
|
|
582
|
+
*
|
|
583
|
+
* This folder contains Mongoose schemas and models for MongoDB.
|
|
584
|
+
*
|
|
585
|
+
* Purpose:
|
|
586
|
+
* - Define data structure and validation rules
|
|
587
|
+
* - Create database models for collections
|
|
588
|
+
* - Add instance and static methods
|
|
589
|
+
* - Set up hooks (pre/post save, etc.)
|
|
590
|
+
*
|
|
591
|
+
* How to create a model:
|
|
592
|
+
*
|
|
593
|
+
* 1. Define interface:
|
|
594
|
+
* export interface IUser extends Document {
|
|
595
|
+
* name: string;
|
|
596
|
+
* email: string;
|
|
597
|
+
* password: string;
|
|
598
|
+
* createdAt: Date;
|
|
599
|
+
* updatedAt: Date;
|
|
600
|
+
* }
|
|
601
|
+
*
|
|
602
|
+
* 2. Create schema:
|
|
603
|
+
* const userSchema = new Schema<IUser>({
|
|
604
|
+
* name: { type: String, required: true, trim: true },
|
|
605
|
+
* email: {
|
|
606
|
+
* type: String,
|
|
607
|
+
* required: true,
|
|
608
|
+
* unique: true,
|
|
609
|
+
* lowercase: true
|
|
610
|
+
* },
|
|
611
|
+
* password: { type: String, required: true, minlength: 6 }
|
|
612
|
+
* }, {
|
|
613
|
+
* timestamps: true // Auto-creates createdAt, updatedAt
|
|
614
|
+
* });
|
|
615
|
+
*
|
|
616
|
+
* 3. Add methods (optional):
|
|
617
|
+
* userSchema.methods.comparePassword = async function(password: string) {
|
|
618
|
+
* return bcrypt.compare(password, this.password);
|
|
619
|
+
* };
|
|
620
|
+
*
|
|
621
|
+
* 4. Export model:
|
|
622
|
+
* export const User = mongoose.model<IUser>('User', userSchema);
|
|
623
|
+
*
|
|
624
|
+
* 5. Use in controllers:
|
|
625
|
+
* import { User } from '../models/user.model';
|
|
626
|
+
*
|
|
627
|
+
* const users = await User.find();
|
|
628
|
+
* const user = await User.create({ name, email, password });
|
|
629
|
+
* const user = await User.findById(id);
|
|
630
|
+
* await User.findByIdAndUpdate(id, updates);
|
|
631
|
+
* await User.findByIdAndDelete(id);
|
|
632
|
+
*
|
|
633
|
+
* Common model files:
|
|
634
|
+
* - user.model.ts - User authentication and profile
|
|
635
|
+
* - product.model.ts - Product catalog
|
|
636
|
+
* - order.model.ts - Customer orders
|
|
637
|
+
*/
|
|
638
|
+
|
|
639
|
+
// Example: Uncomment to use
|
|
640
|
+
// export interface IExample extends Document {
|
|
641
|
+
// field: string;
|
|
642
|
+
// }
|
|
643
|
+
//
|
|
644
|
+
// const exampleSchema = new Schema<IExample>({
|
|
645
|
+
// field: { type: String, required: true }
|
|
646
|
+
// });
|
|
647
|
+
//
|
|
648
|
+
// export const Example = mongoose.model<IExample>('Example', exampleSchema);
|
|
649
|
+
` : `const mongoose = require('mongoose');
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* MODELS FOLDER
|
|
653
|
+
*
|
|
654
|
+
* This folder contains Mongoose schemas and models for MongoDB.
|
|
655
|
+
*
|
|
656
|
+
* Purpose:
|
|
657
|
+
* - Define data structure and validation rules
|
|
658
|
+
* - Create database models for collections
|
|
659
|
+
* - Add instance and static methods
|
|
660
|
+
* - Set up hooks (pre/post save, etc.)
|
|
661
|
+
*
|
|
662
|
+
* How to create a model:
|
|
663
|
+
*
|
|
664
|
+
* 1. Create schema:
|
|
665
|
+
* const userSchema = new mongoose.Schema({
|
|
666
|
+
* name: { type: String, required: true, trim: true },
|
|
667
|
+
* email: {
|
|
668
|
+
* type: String,
|
|
669
|
+
* required: true,
|
|
670
|
+
* unique: true,
|
|
671
|
+
* lowercase: true
|
|
672
|
+
* },
|
|
673
|
+
* password: { type: String, required: true, minlength: 6 }
|
|
674
|
+
* }, {
|
|
675
|
+
* timestamps: true // Auto-creates createdAt, updatedAt
|
|
676
|
+
* });
|
|
677
|
+
*
|
|
678
|
+
* 2. Add methods (optional):
|
|
679
|
+
* userSchema.methods.comparePassword = async function(password) {
|
|
680
|
+
* return bcrypt.compare(password, this.password);
|
|
681
|
+
* };
|
|
682
|
+
*
|
|
683
|
+
* 3. Export model:
|
|
684
|
+
* module.exports = mongoose.model('User', userSchema);
|
|
685
|
+
*
|
|
686
|
+
* 4. Use in controllers:
|
|
687
|
+
* const User = require('../models/user.model');
|
|
688
|
+
*
|
|
689
|
+
* const users = await User.find();
|
|
690
|
+
* const user = await User.create({ name, email, password });
|
|
691
|
+
* const user = await User.findById(id);
|
|
692
|
+
* await User.findByIdAndUpdate(id, updates);
|
|
693
|
+
* await User.findByIdAndDelete(id);
|
|
694
|
+
*
|
|
695
|
+
* Common model files:
|
|
696
|
+
* - user.model.js - User authentication and profile
|
|
697
|
+
* - product.model.js - Product catalog
|
|
698
|
+
* - order.model.js - Customer orders
|
|
699
|
+
*/
|
|
700
|
+
|
|
701
|
+
// Example: Uncomment to use
|
|
702
|
+
// const exampleSchema = new mongoose.Schema({
|
|
703
|
+
// field: { type: String, required: true }
|
|
704
|
+
// });
|
|
705
|
+
//
|
|
706
|
+
// module.exports = mongoose.model('Example', exampleSchema);
|
|
707
|
+
`;
|
|
708
|
+
|
|
709
|
+
writeFile(path.join(projectPath, `src/models/info.${ext}`), mongooseInfoContent);
|
|
710
|
+
|
|
711
|
+
} else if (database === 'postgresql' || database === 'mysql') {
|
|
712
|
+
const sequelizeContent = isTS ? `import { Sequelize } from 'sequelize';
|
|
713
|
+
import { ENV } from './env';
|
|
714
|
+
${logger !== 'None' ? "import logger from './logger';" : ''}
|
|
715
|
+
|
|
716
|
+
export const sequelize = new Sequelize({
|
|
717
|
+
dialect: '${database === 'postgresql' ? 'postgres' : 'mysql'}',
|
|
718
|
+
host: ENV.DB_HOST,
|
|
719
|
+
port: ENV.DB_PORT,
|
|
720
|
+
database: ENV.DB_NAME,
|
|
721
|
+
username: ENV.DB_USER,
|
|
722
|
+
password: ENV.DB_PASSWORD,
|
|
723
|
+
logging: false, // Disable SQL query logging
|
|
724
|
+
pool: {
|
|
725
|
+
max: 5,
|
|
726
|
+
min: 0,
|
|
727
|
+
acquire: 30000,
|
|
728
|
+
idle: 10000
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
export const connectDatabase = async (): Promise<void> => {
|
|
733
|
+
try {
|
|
734
|
+
await sequelize.authenticate();
|
|
735
|
+
${logger !== 'None' ? "logger.info('Database connected successfully');" : "console.log('✅ Database connected');"}
|
|
736
|
+
} catch (error) {
|
|
737
|
+
${logger !== 'None' ? "logger.error('Database connection failed:', error);" : "console.error('❌ Database connection failed:', error);"}
|
|
738
|
+
process.exit(1);
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
` : `const { Sequelize } = require('sequelize');
|
|
742
|
+
const { ENV } = require('./env');
|
|
743
|
+
${logger !== 'None' ? "const logger = require('./logger');" : ''}
|
|
744
|
+
|
|
745
|
+
const sequelize = new Sequelize({
|
|
746
|
+
dialect: '${database === 'postgresql' ? 'postgres' : 'mysql'}',
|
|
747
|
+
host: ENV.DB_HOST,
|
|
748
|
+
port: ENV.DB_PORT,
|
|
749
|
+
database: ENV.DB_NAME,
|
|
750
|
+
username: ENV.DB_USER,
|
|
751
|
+
password: ENV.DB_PASSWORD,
|
|
752
|
+
logging: false, // Disable SQL query logging
|
|
753
|
+
pool: {
|
|
754
|
+
max: 5,
|
|
755
|
+
min: 0,
|
|
756
|
+
acquire: 30000,
|
|
757
|
+
idle: 10000
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
const connectDatabase = async () => {
|
|
762
|
+
try {
|
|
763
|
+
await sequelize.authenticate();
|
|
764
|
+
${logger !== 'None' ? "logger.info('Database connected successfully');" : "console.log('✅ Database connected');"}
|
|
765
|
+
} catch (error) {
|
|
766
|
+
${logger !== 'None' ? "logger.error('Database connection failed:', error);" : "console.error('❌ Database connection failed:', error);"}
|
|
767
|
+
process.exit(1);
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
module.exports = { sequelize, connectDatabase };
|
|
772
|
+
`;
|
|
773
|
+
|
|
774
|
+
writeFile(path.join(projectPath, `src/config/database.${ext}`), sequelizeContent);
|
|
775
|
+
|
|
776
|
+
// Sequelize config file for CLI
|
|
777
|
+
const sequelizeConfigContent = `module.exports = {
|
|
778
|
+
development: {
|
|
779
|
+
username: process.env.DB_USER || '${database === 'postgresql' ? 'postgres' : 'root'}',
|
|
780
|
+
password: process.env.DB_PASSWORD || '${database === 'postgresql' ? 'root' : 'password'}',
|
|
781
|
+
database: process.env.DB_NAME || 'my-back',
|
|
782
|
+
host: process.env.DB_HOST || 'localhost',
|
|
783
|
+
port: process.env.DB_PORT || ${database === 'postgresql' ? '5432' : '3306'},
|
|
784
|
+
dialect: '${database === 'postgresql' ? 'postgres' : 'mysql'}'
|
|
785
|
+
},
|
|
786
|
+
test: {
|
|
787
|
+
username: process.env.DB_USER || '${database === 'postgresql' ? 'postgres' : 'root'}',
|
|
788
|
+
password: process.env.DB_PASSWORD || '${database === 'postgresql' ? 'root' : 'password'}',
|
|
789
|
+
database: \`\${process.env.DB_NAME || 'my-back'}_test\`,
|
|
790
|
+
host: process.env.DB_HOST || 'localhost',
|
|
791
|
+
port: process.env.DB_PORT || ${database === 'postgresql' ? '5432' : '3306'},
|
|
792
|
+
dialect: '${database === 'postgresql' ? 'postgres' : 'mysql'}'
|
|
793
|
+
},
|
|
794
|
+
production: {
|
|
795
|
+
use_env_variable: 'DATABASE_URL',
|
|
796
|
+
dialect: '${database === 'postgresql' ? 'postgres' : 'mysql'}',
|
|
797
|
+
dialectOptions: {
|
|
798
|
+
ssl: {
|
|
799
|
+
require: true,
|
|
800
|
+
rejectUnauthorized: false
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
`;
|
|
806
|
+
|
|
807
|
+
writeFile(path.join(projectPath, '.sequelizerc'), `const path = require('path');
|
|
808
|
+
|
|
809
|
+
module.exports = {
|
|
810
|
+
'config': path.resolve('config', 'database.js'),
|
|
811
|
+
'models-path': path.resolve('src', 'models'),
|
|
812
|
+
'seeders-path': path.resolve('src', 'seeders'),
|
|
813
|
+
'migrations-path': path.resolve('src', 'migrations')
|
|
814
|
+
};
|
|
815
|
+
`);
|
|
816
|
+
|
|
817
|
+
fs.mkdirSync(path.join(projectPath, 'config'), { recursive: true });
|
|
818
|
+
writeFile(path.join(projectPath, 'config/database.js'), sequelizeConfigContent);
|
|
819
|
+
|
|
820
|
+
// Info file for models folder
|
|
821
|
+
const sequelizeInfoContent = isTS ? `import { DataTypes, Model, Optional } from 'sequelize';
|
|
822
|
+
import { sequelize } from '../config/database';
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* MODELS FOLDER
|
|
826
|
+
*
|
|
827
|
+
* This folder contains Sequelize models for ${database === 'postgresql' ? 'PostgreSQL' : 'MySQL'}.
|
|
828
|
+
*
|
|
829
|
+
* Purpose:
|
|
830
|
+
* - Define table structure and data types
|
|
831
|
+
* - Set up relationships between tables
|
|
832
|
+
* - Add validations and constraints
|
|
833
|
+
* - Create model methods and hooks
|
|
834
|
+
*
|
|
835
|
+
* How to create a model:
|
|
836
|
+
*
|
|
837
|
+
* 1. Define interfaces:
|
|
838
|
+
* interface UserAttributes {
|
|
839
|
+
* id: number;
|
|
840
|
+
* name: string;
|
|
841
|
+
* email: string;
|
|
842
|
+
* createdAt?: Date;
|
|
843
|
+
* updatedAt?: Date;
|
|
844
|
+
* }
|
|
845
|
+
*
|
|
846
|
+
* interface UserCreationAttributes extends Optional<UserAttributes, 'id'> {}
|
|
847
|
+
*
|
|
848
|
+
* 2. Create model class:
|
|
849
|
+
* export class User extends Model<UserAttributes, UserCreationAttributes>
|
|
850
|
+
* implements UserAttributes {
|
|
851
|
+
* public id!: number;
|
|
852
|
+
* public name!: string;
|
|
853
|
+
* public email!: string;
|
|
854
|
+
* public readonly createdAt!: Date;
|
|
855
|
+
* public readonly updatedAt!: Date;
|
|
856
|
+
* }
|
|
857
|
+
*
|
|
858
|
+
* 3. Initialize model:
|
|
859
|
+
* User.init({
|
|
860
|
+
* id: {
|
|
861
|
+
* type: DataTypes.INTEGER,
|
|
862
|
+
* autoIncrement: true,
|
|
863
|
+
* primaryKey: true
|
|
864
|
+
* },
|
|
865
|
+
* name: {
|
|
866
|
+
* type: DataTypes.STRING,
|
|
867
|
+
* allowNull: false
|
|
868
|
+
* },
|
|
869
|
+
* email: {
|
|
870
|
+
* type: DataTypes.STRING,
|
|
871
|
+
* allowNull: false,
|
|
872
|
+
* unique: true,
|
|
873
|
+
* validate: { isEmail: true }
|
|
874
|
+
* }
|
|
875
|
+
* }, {
|
|
876
|
+
* sequelize,
|
|
877
|
+
* tableName: 'users',
|
|
878
|
+
* timestamps: true // Auto-creates createdAt, updatedAt
|
|
879
|
+
* });
|
|
880
|
+
*
|
|
881
|
+
* 4. Add associations (optional):
|
|
882
|
+
* User.hasMany(Post, { foreignKey: 'userId' });
|
|
883
|
+
* Post.belongsTo(User, { foreignKey: 'userId' });
|
|
884
|
+
*
|
|
885
|
+
* 5. Use in controllers:
|
|
886
|
+
* import { User } from '../models/user.model';
|
|
887
|
+
*
|
|
888
|
+
* const users = await User.findAll();
|
|
889
|
+
* const user = await User.create({ name, email });
|
|
890
|
+
* const user = await User.findByPk(id);
|
|
891
|
+
* await User.update({ name }, { where: { id } });
|
|
892
|
+
* await User.destroy({ where: { id } });
|
|
893
|
+
*
|
|
894
|
+
* Generate migration:
|
|
895
|
+
* npx sequelize-cli migration:generate --name create-users
|
|
896
|
+
*
|
|
897
|
+
* Common model files:
|
|
898
|
+
* - user.model.ts - User authentication and profile
|
|
899
|
+
* - product.model.ts - Product catalog
|
|
900
|
+
* - order.model.ts - Customer orders
|
|
901
|
+
*/
|
|
902
|
+
|
|
903
|
+
// Example: Uncomment to use
|
|
904
|
+
// interface ExampleAttributes {
|
|
905
|
+
// id: number;
|
|
906
|
+
// field: string;
|
|
907
|
+
// }
|
|
908
|
+
//
|
|
909
|
+
// interface ExampleCreationAttributes extends Optional<ExampleAttributes, 'id'> {}
|
|
910
|
+
//
|
|
911
|
+
// export class Example extends Model<ExampleAttributes, ExampleCreationAttributes> {
|
|
912
|
+
// public id!: number;
|
|
913
|
+
// public field!: string;
|
|
914
|
+
// }
|
|
915
|
+
//
|
|
916
|
+
// Example.init({
|
|
917
|
+
// id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
|
|
918
|
+
// field: { type: DataTypes.STRING, allowNull: false }
|
|
919
|
+
// }, { sequelize, tableName: 'examples' });
|
|
920
|
+
` : `const { DataTypes, Model } = require('sequelize');
|
|
921
|
+
const { sequelize } = require('../config/database');
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* MODELS FOLDER
|
|
925
|
+
*
|
|
926
|
+
* This folder contains Sequelize models for ${database === 'postgresql' ? 'PostgreSQL' : 'MySQL'}.
|
|
927
|
+
*
|
|
928
|
+
* Purpose:
|
|
929
|
+
* - Define table structure and data types
|
|
930
|
+
* - Set up relationships between tables
|
|
931
|
+
* - Add validations and constraints
|
|
932
|
+
* - Create model methods and hooks
|
|
933
|
+
*
|
|
934
|
+
* How to create a model:
|
|
935
|
+
*
|
|
936
|
+
* 1. Create model class:
|
|
937
|
+
* class User extends Model {}
|
|
938
|
+
*
|
|
939
|
+
* 2. Initialize model:
|
|
940
|
+
* User.init({
|
|
941
|
+
* id: {
|
|
942
|
+
* type: DataTypes.INTEGER,
|
|
943
|
+
* autoIncrement: true,
|
|
944
|
+
* primaryKey: true
|
|
945
|
+
* },
|
|
946
|
+
* name: {
|
|
947
|
+
* type: DataTypes.STRING,
|
|
948
|
+
* allowNull: false
|
|
949
|
+
* },
|
|
950
|
+
* email: {
|
|
951
|
+
* type: DataTypes.STRING,
|
|
952
|
+
* allowNull: false,
|
|
953
|
+
* unique: true,
|
|
954
|
+
* validate: { isEmail: true }
|
|
955
|
+
* }
|
|
956
|
+
* }, {
|
|
957
|
+
* sequelize,
|
|
958
|
+
* tableName: 'users',
|
|
959
|
+
* timestamps: true // Auto-creates createdAt, updatedAt
|
|
960
|
+
* });
|
|
961
|
+
*
|
|
962
|
+
* 3. Add associations (optional):
|
|
963
|
+
* User.hasMany(Post, { foreignKey: 'userId' });
|
|
964
|
+
* Post.belongsTo(User, { foreignKey: 'userId' });
|
|
965
|
+
*
|
|
966
|
+
* 4. Use in controllers:
|
|
967
|
+
* const User = require('../models/user.model');
|
|
968
|
+
*
|
|
969
|
+
* const users = await User.findAll();
|
|
970
|
+
* const user = await User.create({ name, email });
|
|
971
|
+
* const user = await User.findByPk(id);
|
|
972
|
+
* await User.update({ name }, { where: { id } });
|
|
973
|
+
* await User.destroy({ where: { id } });
|
|
974
|
+
*
|
|
975
|
+
* Generate migration:
|
|
976
|
+
* npx sequelize-cli migration:generate --name create-users
|
|
977
|
+
*
|
|
978
|
+
* Common model files:
|
|
979
|
+
* - user.model.js - User authentication and profile
|
|
980
|
+
* - product.model.js - Product catalog
|
|
981
|
+
* - order.model.js - Customer orders
|
|
982
|
+
*/
|
|
983
|
+
|
|
984
|
+
// Example: Uncomment to use
|
|
985
|
+
// class Example extends Model {}
|
|
986
|
+
//
|
|
987
|
+
// Example.init({
|
|
988
|
+
// id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
|
|
989
|
+
// field: { type: DataTypes.STRING, allowNull: false }
|
|
990
|
+
// }, { sequelize, tableName: 'examples' });
|
|
991
|
+
//
|
|
992
|
+
// module.exports = { Example };
|
|
993
|
+
`;
|
|
994
|
+
|
|
995
|
+
writeFile(path.join(projectPath, `src/models/info.${ext}`), sequelizeInfoContent);
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// ============================================================
|
|
999
|
+
// 11. LOGGER CONFIG - UPDATED WITH CLEAN OUTPUT
|
|
1000
|
+
// ============================================================
|
|
1001
|
+
if (logger === 'Winston') {
|
|
1002
|
+
const winstonContent = isTS ? `import winston from 'winston';
|
|
1003
|
+
|
|
1004
|
+
const logger = winston.createLogger({
|
|
1005
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
1006
|
+
format: winston.format.combine(
|
|
1007
|
+
winston.format.errors({ stack: true }),
|
|
1008
|
+
winston.format.splat(),
|
|
1009
|
+
winston.format.printf(({ level, message }) => {
|
|
1010
|
+
const emoji = level === 'info' ? '✅' : level === 'error' ? '❌' : 'ℹ️';
|
|
1011
|
+
return \`\${emoji} \${message}\`;
|
|
1012
|
+
})
|
|
1013
|
+
),
|
|
1014
|
+
transports: [
|
|
1015
|
+
new winston.transports.Console({
|
|
1016
|
+
format: winston.format.combine(
|
|
1017
|
+
winston.format.colorize(),
|
|
1018
|
+
winston.format.printf(({ level, message }) => {
|
|
1019
|
+
const emoji = level.includes('info') ? '✅' : level.includes('error') ? '❌' : 'ℹ️';
|
|
1020
|
+
return \`\${emoji} \${message}\`;
|
|
1021
|
+
})
|
|
1022
|
+
),
|
|
1023
|
+
}),
|
|
1024
|
+
],
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
export default logger;
|
|
1028
|
+
` : `const winston = require('winston');
|
|
1029
|
+
|
|
1030
|
+
const logger = winston.createLogger({
|
|
1031
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
1032
|
+
format: winston.format.combine(
|
|
1033
|
+
winston.format.errors({ stack: true }),
|
|
1034
|
+
winston.format.splat(),
|
|
1035
|
+
winston.format.printf(({ level, message }) => {
|
|
1036
|
+
const emoji = level === 'info' ? '✅' : level === 'error' ? '❌' : 'ℹ️';
|
|
1037
|
+
return \`\${emoji} \${message}\`;
|
|
1038
|
+
})
|
|
1039
|
+
),
|
|
1040
|
+
transports: [
|
|
1041
|
+
new winston.transports.Console({
|
|
1042
|
+
format: winston.format.combine(
|
|
1043
|
+
winston.format.colorize(),
|
|
1044
|
+
winston.format.printf(({ level, message }) => {
|
|
1045
|
+
const emoji = level.includes('info') ? '✅' : level.includes('error') ? '❌' : 'ℹ️';
|
|
1046
|
+
return \`\${emoji} \${message}\`;
|
|
1047
|
+
})
|
|
1048
|
+
),
|
|
1049
|
+
}),
|
|
1050
|
+
],
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
module.exports = logger;
|
|
1054
|
+
`;
|
|
1055
|
+
|
|
1056
|
+
writeFile(path.join(projectPath, `src/config/logger.${ext}`), winstonContent);
|
|
1057
|
+
} else if (logger === 'Pino') {
|
|
1058
|
+
const pinoContent = isTS ? `import pino from 'pino';
|
|
1059
|
+
import { ENV } from './env';
|
|
1060
|
+
|
|
1061
|
+
const logger = pino({
|
|
1062
|
+
level: ENV.NODE_ENV === 'production' ? 'info' : 'debug',
|
|
1063
|
+
transport: ENV.NODE_ENV === 'development' ? {
|
|
1064
|
+
target: 'pino-pretty',
|
|
1065
|
+
options: {
|
|
1066
|
+
colorize: true,
|
|
1067
|
+
translateTime: 'SYS:standard',
|
|
1068
|
+
ignore: 'pid,hostname'
|
|
1069
|
+
}
|
|
1070
|
+
} : undefined
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
export default logger;
|
|
1074
|
+
` : `const pino = require('pino');
|
|
1075
|
+
const { ENV } = require('./env');
|
|
1076
|
+
|
|
1077
|
+
const logger = pino({
|
|
1078
|
+
level: ENV.NODE_ENV === 'production' ? 'info' : 'debug',
|
|
1079
|
+
transport: ENV.NODE_ENV === 'development' ? {
|
|
1080
|
+
target: 'pino-pretty',
|
|
1081
|
+
options: {
|
|
1082
|
+
colorize: true,
|
|
1083
|
+
translateTime: 'SYS:standard',
|
|
1084
|
+
ignore: 'pid,hostname'
|
|
1085
|
+
}
|
|
1086
|
+
} : undefined
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
module.exports = logger;
|
|
1090
|
+
`;
|
|
1091
|
+
|
|
1092
|
+
writeFile(path.join(projectPath, `src/config/logger.${ext}`), pinoContent);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// ============================================================
|
|
1096
|
+
// 12. VALIDATORS (if validation library chosen)
|
|
1097
|
+
// ============================================================
|
|
1098
|
+
if (validation === 'zod') {
|
|
1099
|
+
const zodValidatorContent = isTS ? `import { z } from 'zod';
|
|
1100
|
+
|
|
1101
|
+
/**
|
|
1102
|
+
* VALIDATORS FOLDER
|
|
1103
|
+
*
|
|
1104
|
+
* This folder contains request validation schemas using Zod.
|
|
1105
|
+
*
|
|
1106
|
+
* Purpose:
|
|
1107
|
+
* - Validate incoming request data (body, params, query)
|
|
1108
|
+
* - Ensure type safety with TypeScript inference
|
|
1109
|
+
* - Provide clear error messages for invalid data
|
|
1110
|
+
*
|
|
1111
|
+
* How to create a validator:
|
|
1112
|
+
*
|
|
1113
|
+
* 1. Define your schema:
|
|
1114
|
+
* export const createUserSchema = z.object({
|
|
1115
|
+
* name: z.string().min(1, 'Name is required'),
|
|
1116
|
+
* email: z.string().email('Invalid email'),
|
|
1117
|
+
* age: z.number().min(18).optional()
|
|
1118
|
+
* });
|
|
1119
|
+
*
|
|
1120
|
+
* 2. Infer TypeScript type:
|
|
1121
|
+
* export type CreateUserInput = z.infer<typeof createUserSchema>;
|
|
1122
|
+
*
|
|
1123
|
+
* 3. Use in middleware:
|
|
1124
|
+
* const validateRequest = (schema: z.ZodSchema) => {
|
|
1125
|
+
* return (req, res, next) => {
|
|
1126
|
+
* const result = schema.safeParse(req.body);
|
|
1127
|
+
* if (!result.success) {
|
|
1128
|
+
* return res.status(400).json({
|
|
1129
|
+
* success: false,
|
|
1130
|
+
* errors: result.error.errors
|
|
1131
|
+
* });
|
|
1132
|
+
* }
|
|
1133
|
+
* req.body = result.data;
|
|
1134
|
+
* next();
|
|
1135
|
+
* };
|
|
1136
|
+
* };
|
|
1137
|
+
*
|
|
1138
|
+
* Example validators for common use cases:
|
|
1139
|
+
* - user.validator.ts - User registration, login, update
|
|
1140
|
+
* - product.validator.ts - Product creation, update
|
|
1141
|
+
* - auth.validator.ts - Authentication requests
|
|
1142
|
+
*/
|
|
1143
|
+
|
|
1144
|
+
// Example: Uncomment to use
|
|
1145
|
+
// export const exampleSchema = z.object({
|
|
1146
|
+
// field: z.string()
|
|
1147
|
+
// });
|
|
1148
|
+
` : `const { z } = require('zod');
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* VALIDATORS FOLDER
|
|
1152
|
+
*
|
|
1153
|
+
* This folder contains request validation schemas using Zod.
|
|
1154
|
+
*
|
|
1155
|
+
* Purpose:
|
|
1156
|
+
* - Validate incoming request data (body, params, query)
|
|
1157
|
+
* - Provide clear error messages for invalid data
|
|
1158
|
+
*
|
|
1159
|
+
* How to create a validator:
|
|
1160
|
+
*
|
|
1161
|
+
* 1. Define your schema:
|
|
1162
|
+
* const createUserSchema = z.object({
|
|
1163
|
+
* name: z.string().min(1, 'Name is required'),
|
|
1164
|
+
* email: z.string().email('Invalid email'),
|
|
1165
|
+
* age: z.number().min(18).optional()
|
|
1166
|
+
* });
|
|
1167
|
+
*
|
|
1168
|
+
* 2. Use in middleware:
|
|
1169
|
+
* const validateRequest = (schema) => {
|
|
1170
|
+
* return (req, res, next) => {
|
|
1171
|
+
* const result = schema.safeParse(req.body);
|
|
1172
|
+
* if (!result.success) {
|
|
1173
|
+
* return res.status(400).json({
|
|
1174
|
+
* success: false,
|
|
1175
|
+
* errors: result.error.errors
|
|
1176
|
+
* });
|
|
1177
|
+
* }
|
|
1178
|
+
* req.body = result.data;
|
|
1179
|
+
* next();
|
|
1180
|
+
* };
|
|
1181
|
+
* };
|
|
1182
|
+
*
|
|
1183
|
+
* Example validators for common use cases:
|
|
1184
|
+
* - user.validator.js - User registration, login, update
|
|
1185
|
+
* - product.validator.js - Product creation, update
|
|
1186
|
+
* - auth.validator.js - Authentication requests
|
|
1187
|
+
*/
|
|
1188
|
+
|
|
1189
|
+
// Example: Uncomment to use
|
|
1190
|
+
// const exampleSchema = z.object({
|
|
1191
|
+
// field: z.string()
|
|
1192
|
+
// });
|
|
1193
|
+
//
|
|
1194
|
+
// module.exports = { exampleSchema };
|
|
1195
|
+
`;
|
|
1196
|
+
|
|
1197
|
+
writeFile(path.join(projectPath, `src/validators/info.${ext}`), zodValidatorContent);
|
|
1198
|
+
} else if (validation === 'joi') {
|
|
1199
|
+
const joiValidatorContent = isTS ? `import Joi from 'joi';
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* VALIDATORS FOLDER
|
|
1203
|
+
*
|
|
1204
|
+
* This folder contains request validation schemas using Joi.
|
|
1205
|
+
*
|
|
1206
|
+
* Purpose:
|
|
1207
|
+
* - Validate incoming request data (body, params, query)
|
|
1208
|
+
* - Provide clear error messages for invalid data
|
|
1209
|
+
*
|
|
1210
|
+
* How to create a validator:
|
|
1211
|
+
*
|
|
1212
|
+
* 1. Define your schema:
|
|
1213
|
+
* export const createUserSchema = Joi.object({
|
|
1214
|
+
* name: Joi.string().required(),
|
|
1215
|
+
* email: Joi.string().email().required(),
|
|
1216
|
+
* age: Joi.number().min(18).optional()
|
|
1217
|
+
* });
|
|
1218
|
+
*
|
|
1219
|
+
* 2. Use in middleware:
|
|
1220
|
+
* const validateRequest = (schema: Joi.Schema) => {
|
|
1221
|
+
* return (req, res, next) => {
|
|
1222
|
+
* const { error, value } = schema.validate(req.body);
|
|
1223
|
+
* if (error) {
|
|
1224
|
+
* return res.status(400).json({
|
|
1225
|
+
* success: false,
|
|
1226
|
+
* errors: error.details
|
|
1227
|
+
* });
|
|
1228
|
+
* }
|
|
1229
|
+
* req.body = value;
|
|
1230
|
+
* next();
|
|
1231
|
+
* };
|
|
1232
|
+
* };
|
|
1233
|
+
*
|
|
1234
|
+
* Example validators for common use cases:
|
|
1235
|
+
* - user.validator.ts - User registration, login, update
|
|
1236
|
+
* - product.validator.ts - Product creation, update
|
|
1237
|
+
* - auth.validator.ts - Authentication requests
|
|
1238
|
+
*/
|
|
1239
|
+
|
|
1240
|
+
// Example: Uncomment to use
|
|
1241
|
+
// export const exampleSchema = Joi.object({
|
|
1242
|
+
// field: Joi.string().required()
|
|
1243
|
+
// });
|
|
1244
|
+
` : `const Joi = require('joi');
|
|
1245
|
+
|
|
1246
|
+
/**
|
|
1247
|
+
* VALIDATORS FOLDER
|
|
1248
|
+
*
|
|
1249
|
+
* This folder contains request validation schemas using Joi.
|
|
1250
|
+
*
|
|
1251
|
+
* Purpose:
|
|
1252
|
+
* - Validate incoming request data (body, params, query)
|
|
1253
|
+
* - Provide clear error messages for invalid data
|
|
1254
|
+
*
|
|
1255
|
+
* How to create a validator:
|
|
1256
|
+
*
|
|
1257
|
+
* 1. Define your schema:
|
|
1258
|
+
* const createUserSchema = Joi.object({
|
|
1259
|
+
* name: Joi.string().required(),
|
|
1260
|
+
* email: Joi.string().email().required(),
|
|
1261
|
+
* age: Joi.number().min(18).optional()
|
|
1262
|
+
* });
|
|
1263
|
+
*
|
|
1264
|
+
* 2. Use in middleware:
|
|
1265
|
+
* const validateRequest = (schema) => {
|
|
1266
|
+
* return (req, res, next) => {
|
|
1267
|
+
* const { error, value } = schema.validate(req.body);
|
|
1268
|
+
* if (error) {
|
|
1269
|
+
* return res.status(400).json({
|
|
1270
|
+
* success: false,
|
|
1271
|
+
* errors: error.details
|
|
1272
|
+
* });
|
|
1273
|
+
* }
|
|
1274
|
+
* req.body = value;
|
|
1275
|
+
* next();
|
|
1276
|
+
* };
|
|
1277
|
+
* };
|
|
1278
|
+
*
|
|
1279
|
+
* Example validators for common use cases:
|
|
1280
|
+
* - user.validator.js - User registration, login, update
|
|
1281
|
+
* - product.validator.js - Product creation, update
|
|
1282
|
+
* - auth.validator.js - Authentication requests
|
|
1283
|
+
*/
|
|
1284
|
+
|
|
1285
|
+
// Example: Uncomment to use
|
|
1286
|
+
// const exampleSchema = Joi.object({
|
|
1287
|
+
// field: Joi.string().required()
|
|
1288
|
+
// });
|
|
1289
|
+
//
|
|
1290
|
+
// module.exports = { exampleSchema };
|
|
1291
|
+
`;
|
|
1292
|
+
|
|
1293
|
+
writeFile(path.join(projectPath, `src/validators/info.${ext}`), joiValidatorContent);
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
// ============================================================
|
|
1297
|
+
// 13. INFO FILES FOR FOLDER STRUCTURE
|
|
1298
|
+
// ============================================================
|
|
1299
|
+
|
|
1300
|
+
// Routes info
|
|
1301
|
+
writeFile(path.join(projectPath, `src/routes/info.${ext}`), isTS
|
|
1302
|
+
? `import { Router } from 'express';
|
|
1303
|
+
|
|
1304
|
+
/**
|
|
1305
|
+
* ROUTES FOLDER
|
|
1306
|
+
*
|
|
1307
|
+
* This folder contains API route definitions.
|
|
1308
|
+
*
|
|
1309
|
+
* Purpose:
|
|
1310
|
+
* - Define API endpoints and HTTP methods
|
|
1311
|
+
* - Map routes to controller functions
|
|
1312
|
+
* - Apply middleware (auth, validation, etc.)
|
|
1313
|
+
* - Group related routes together
|
|
1314
|
+
*
|
|
1315
|
+
* How to create routes:
|
|
1316
|
+
*
|
|
1317
|
+
* 1. Create a router:
|
|
1318
|
+
* import { Router } from 'express';
|
|
1319
|
+
* const router = Router();
|
|
1320
|
+
*
|
|
1321
|
+
* 2. Define routes:
|
|
1322
|
+
* import { getUsers, createUser } from '../controllers/user.controller';
|
|
1323
|
+
* import { authenticate } from '../middlewares/auth.middleware';
|
|
1324
|
+
* import { validateRequest } from '../middlewares/validation.middleware';
|
|
1325
|
+
* import { createUserSchema } from '../validators/user.validator';
|
|
1326
|
+
*
|
|
1327
|
+
* router.get('/', authenticate, getUsers);
|
|
1328
|
+
* router.post('/',
|
|
1329
|
+
* authenticate,
|
|
1330
|
+
* validateRequest(createUserSchema),
|
|
1331
|
+
* createUser
|
|
1332
|
+
* );
|
|
1333
|
+
* router.get('/:id', authenticate, getUserById);
|
|
1334
|
+
* router.put('/:id', authenticate, updateUser);
|
|
1335
|
+
* router.delete('/:id', authenticate, deleteUser);
|
|
1336
|
+
*
|
|
1337
|
+
* 3. Export router:
|
|
1338
|
+
* export default router;
|
|
1339
|
+
*
|
|
1340
|
+
* 4. Register in main routes:
|
|
1341
|
+
* import userRoutes from './user.routes';
|
|
1342
|
+
* router.use('/users', userRoutes);
|
|
1343
|
+
*
|
|
1344
|
+
* Route naming conventions:
|
|
1345
|
+
* - user.routes.ts - User-related endpoints
|
|
1346
|
+
* - auth.routes.ts - Authentication endpoints
|
|
1347
|
+
* - product.routes.ts - Product endpoints
|
|
1348
|
+
*
|
|
1349
|
+
* Example structure:
|
|
1350
|
+
* GET /api/users - Get all users
|
|
1351
|
+
* POST /api/users - Create user
|
|
1352
|
+
* GET /api/users/:id - Get user by ID
|
|
1353
|
+
* PUT /api/users/:id - Update user
|
|
1354
|
+
* DELETE /api/users/:id - Delete user
|
|
1355
|
+
*/
|
|
1356
|
+
|
|
1357
|
+
const router = Router();
|
|
1358
|
+
|
|
1359
|
+
// Example: Uncomment to use
|
|
1360
|
+
// import exampleRoutes from './example.routes';
|
|
1361
|
+
// router.use('/examples', exampleRoutes);
|
|
1362
|
+
|
|
1363
|
+
export default router;`
|
|
1364
|
+
: `const { Router } = require('express');
|
|
1365
|
+
|
|
1366
|
+
/**
|
|
1367
|
+
* ROUTES FOLDER
|
|
1368
|
+
*
|
|
1369
|
+
* This folder contains API route definitions.
|
|
1370
|
+
*
|
|
1371
|
+
* Purpose:
|
|
1372
|
+
* - Define API endpoints and HTTP methods
|
|
1373
|
+
* - Map routes to controller functions
|
|
1374
|
+
* - Apply middleware (auth, validation, etc.)
|
|
1375
|
+
* - Group related routes together
|
|
1376
|
+
*
|
|
1377
|
+
* How to create routes:
|
|
1378
|
+
*
|
|
1379
|
+
* 1. Create a router:
|
|
1380
|
+
* const { Router } = require('express');
|
|
1381
|
+
* const router = Router();
|
|
1382
|
+
*
|
|
1383
|
+
* 2. Define routes:
|
|
1384
|
+
* const { getUsers, createUser } = require('../controllers/user.controller');
|
|
1385
|
+
* const { authenticate } = require('../middlewares/auth.middleware');
|
|
1386
|
+
* const { validateRequest } = require('../middlewares/validation.middleware');
|
|
1387
|
+
* const { createUserSchema } = require('../validators/user.validator');
|
|
1388
|
+
*
|
|
1389
|
+
* router.get('/', authenticate, getUsers);
|
|
1390
|
+
* router.post('/',
|
|
1391
|
+
* authenticate,
|
|
1392
|
+
* validateRequest(createUserSchema),
|
|
1393
|
+
* createUser
|
|
1394
|
+
* );
|
|
1395
|
+
* router.get('/:id', authenticate, getUserById);
|
|
1396
|
+
* router.put('/:id', authenticate, updateUser);
|
|
1397
|
+
* router.delete('/:id', authenticate, deleteUser);
|
|
1398
|
+
*
|
|
1399
|
+
* 3. Export router:
|
|
1400
|
+
* module.exports = router;
|
|
1401
|
+
*
|
|
1402
|
+
* 4. Register in main routes:
|
|
1403
|
+
* const userRoutes = require('./user.routes');
|
|
1404
|
+
* router.use('/users', userRoutes);
|
|
1405
|
+
*
|
|
1406
|
+
* Route naming conventions:
|
|
1407
|
+
* - user.routes.js - User-related endpoints
|
|
1408
|
+
* - auth.routes.js - Authentication endpoints
|
|
1409
|
+
* - product.routes.js - Product endpoints
|
|
1410
|
+
*
|
|
1411
|
+
* Example structure:
|
|
1412
|
+
* GET /api/users - Get all users
|
|
1413
|
+
* POST /api/users - Create user
|
|
1414
|
+
* GET /api/users/:id - Get user by ID
|
|
1415
|
+
* PUT /api/users/:id - Update user
|
|
1416
|
+
* DELETE /api/users/:id - Delete user
|
|
1417
|
+
*/
|
|
1418
|
+
|
|
1419
|
+
const router = Router();
|
|
1420
|
+
|
|
1421
|
+
// Example: Uncomment to use
|
|
1422
|
+
// const exampleRoutes = require('./example.routes');
|
|
1423
|
+
// router.use('/examples', exampleRoutes);
|
|
1424
|
+
|
|
1425
|
+
module.exports = router;`
|
|
1426
|
+
);
|
|
1427
|
+
|
|
1428
|
+
// Controllers info
|
|
1429
|
+
writeFile(path.join(projectPath, `src/controllers/info.${ext}`), isTS
|
|
1430
|
+
? `import { Request, Response, NextFunction } from 'express';
|
|
1431
|
+
${errorHandling ? "import { successResponse, errorResponse } from '../utils/response';\nimport { AppError } from '../utils/AppError';" : ''}
|
|
1432
|
+
|
|
1433
|
+
/**
|
|
1434
|
+
* CONTROLLERS FOLDER
|
|
1435
|
+
*
|
|
1436
|
+
* This folder contains controller functions that handle HTTP requests.
|
|
1437
|
+
*
|
|
1438
|
+
* Purpose:
|
|
1439
|
+
* - Process incoming requests
|
|
1440
|
+
* - Call service layer for business logic
|
|
1441
|
+
* - Return HTTP responses
|
|
1442
|
+
* - Handle errors appropriately
|
|
1443
|
+
*
|
|
1444
|
+
* How to create a controller:
|
|
1445
|
+
*
|
|
1446
|
+
* 1. Import dependencies:
|
|
1447
|
+
* import { Request, Response, NextFunction } from 'express';
|
|
1448
|
+
${errorHandling ? "* import { successResponse, errorResponse } from '../utils/response';" : ''}
|
|
1449
|
+
* import { UserService } from '../services/user.service';
|
|
1450
|
+
*
|
|
1451
|
+
* 2. Create controller functions:
|
|
1452
|
+
* export const getUsers = async (
|
|
1453
|
+
* req: Request,
|
|
1454
|
+
* res: Response,
|
|
1455
|
+
* next: NextFunction
|
|
1456
|
+
* ) => {
|
|
1457
|
+
* try {
|
|
1458
|
+
* const users = await UserService.getAllUsers();
|
|
1459
|
+
${errorHandling ? "* return successResponse(res, users, 'Users retrieved successfully');" : "* return res.json({ success: true, data: users });"}
|
|
1460
|
+
* } catch (error) {
|
|
1461
|
+
* next(error);
|
|
1462
|
+
* }
|
|
1463
|
+
* };
|
|
1464
|
+
*
|
|
1465
|
+
* export const createUser = async (
|
|
1466
|
+
* req: Request,
|
|
1467
|
+
* res: Response,
|
|
1468
|
+
* next: NextFunction
|
|
1469
|
+
* ) => {
|
|
1470
|
+
* try {
|
|
1471
|
+
* const user = await UserService.createUser(req.body);
|
|
1472
|
+
${errorHandling ? "* return successResponse(res, user, 'User created', 201);" : "* return res.status(201).json({ success: true, data: user });"}
|
|
1473
|
+
* } catch (error) {
|
|
1474
|
+
* next(error);
|
|
1475
|
+
* }
|
|
1476
|
+
* };
|
|
1477
|
+
*
|
|
1478
|
+
* Controller best practices:
|
|
1479
|
+
* - Keep controllers thin - delegate logic to services
|
|
1480
|
+
* - Always use try-catch and pass errors to next()
|
|
1481
|
+
* - Use consistent response format
|
|
1482
|
+
* - Validate input (use validators + middleware)
|
|
1483
|
+
* - Return appropriate HTTP status codes
|
|
1484
|
+
*
|
|
1485
|
+
* Common controller files:
|
|
1486
|
+
* - user.controller.ts - User CRUD operations
|
|
1487
|
+
* - auth.controller.ts - Login, register, logout
|
|
1488
|
+
* - product.controller.ts - Product management
|
|
1489
|
+
*/
|
|
1490
|
+
|
|
1491
|
+
// Example: Uncomment to use
|
|
1492
|
+
// export const exampleController = async (
|
|
1493
|
+
// req: Request,
|
|
1494
|
+
// res: Response,
|
|
1495
|
+
// next: NextFunction
|
|
1496
|
+
// ) => {
|
|
1497
|
+
// try {
|
|
1498
|
+
// // Your logic here
|
|
1499
|
+
${errorHandling ? "// return successResponse(res, {}, 'Success');" : "// return res.json({ success: true });"}
|
|
1500
|
+
// } catch (error) {
|
|
1501
|
+
// next(error);
|
|
1502
|
+
// }
|
|
1503
|
+
// };`
|
|
1504
|
+
: `${errorHandling ? "const { successResponse, errorResponse } = require('../utils/response');\nconst { AppError } = require('../utils/AppError');" : ''}
|
|
1505
|
+
|
|
1506
|
+
/**
|
|
1507
|
+
* CONTROLLERS FOLDER
|
|
1508
|
+
*
|
|
1509
|
+
* This folder contains controller functions that handle HTTP requests.
|
|
1510
|
+
*
|
|
1511
|
+
* Purpose:
|
|
1512
|
+
* - Process incoming requests
|
|
1513
|
+
* - Call service layer for business logic
|
|
1514
|
+
* - Return HTTP responses
|
|
1515
|
+
* - Handle errors appropriately
|
|
1516
|
+
*
|
|
1517
|
+
* How to create a controller:
|
|
1518
|
+
*
|
|
1519
|
+
* 1. Import dependencies:
|
|
1520
|
+
${errorHandling ? "* const { successResponse } = require('../utils/response');" : ''}
|
|
1521
|
+
* const UserService = require('../services/user.service');
|
|
1522
|
+
*
|
|
1523
|
+
* 2. Create controller functions:
|
|
1524
|
+
* const getUsers = async (req, res, next) => {
|
|
1525
|
+
* try {
|
|
1526
|
+
* const users = await UserService.getAllUsers();
|
|
1527
|
+
${errorHandling ? "* return successResponse(res, users, 'Users retrieved successfully');" : "* return res.json({ success: true, data: users });"}
|
|
1528
|
+
* } catch (error) {
|
|
1529
|
+
* next(error);
|
|
1530
|
+
* }
|
|
1531
|
+
* };
|
|
1532
|
+
*
|
|
1533
|
+
* const createUser = async (req, res, next) => {
|
|
1534
|
+
* try {
|
|
1535
|
+
* const user = await UserService.createUser(req.body);
|
|
1536
|
+
${errorHandling ? "* return successResponse(res, user, 'User created', 201);" : "* return res.status(201).json({ success: true, data: user });"}
|
|
1537
|
+
* } catch (error) {
|
|
1538
|
+
* next(error);
|
|
1539
|
+
* }
|
|
1540
|
+
* };
|
|
1541
|
+
*
|
|
1542
|
+
* 3. Export functions:
|
|
1543
|
+
* module.exports = { getUsers, createUser };
|
|
1544
|
+
*
|
|
1545
|
+
* Controller best practices:
|
|
1546
|
+
* - Keep controllers thin - delegate logic to services
|
|
1547
|
+
* - Always use try-catch and pass errors to next()
|
|
1548
|
+
* - Use consistent response format
|
|
1549
|
+
* - Validate input (use validators + middleware)
|
|
1550
|
+
* - Return appropriate HTTP status codes
|
|
1551
|
+
*
|
|
1552
|
+
* Common controller files:
|
|
1553
|
+
* - user.controller.js - User CRUD operations
|
|
1554
|
+
* - auth.controller.js - Login, register, logout
|
|
1555
|
+
* - product.controller.js - Product management
|
|
1556
|
+
*/
|
|
1557
|
+
|
|
1558
|
+
// Example: Uncomment to use
|
|
1559
|
+
// const exampleController = async (req, res, next) => {
|
|
1560
|
+
// try {
|
|
1561
|
+
// // Your logic here
|
|
1562
|
+
${errorHandling ? "// return successResponse(res, {}, 'Success');" : "// return res.json({ success: true });"}
|
|
1563
|
+
// } catch (error) {
|
|
1564
|
+
// next(error);
|
|
1565
|
+
// }
|
|
1566
|
+
// };
|
|
1567
|
+
//
|
|
1568
|
+
// module.exports = { exampleController };`
|
|
1569
|
+
);
|
|
1570
|
+
|
|
1571
|
+
// Services info
|
|
1572
|
+
writeFile(path.join(projectPath, `src/services/info.${ext}`), isTS
|
|
1573
|
+
? `${database === 'mongodb' ? "// import { User } from '../models/user.model';" : database === 'postgresql' || database === 'mysql' ? "// import { User } from '../models/user.model';" : ''}
|
|
1574
|
+
${errorHandling ? "import { AppError } from '../utils/AppError';" : ''}
|
|
1575
|
+
|
|
1576
|
+
/**
|
|
1577
|
+
* SERVICES FOLDER
|
|
1578
|
+
*
|
|
1579
|
+
* This folder contains business logic and data access layer.
|
|
1580
|
+
*
|
|
1581
|
+
* Purpose:
|
|
1582
|
+
* - Encapsulate business logic
|
|
1583
|
+
* - Interact with database models
|
|
1584
|
+
* - Handle data transformations
|
|
1585
|
+
* - Reusable functions across controllers
|
|
1586
|
+
*
|
|
1587
|
+
* How to create a service:
|
|
1588
|
+
*
|
|
1589
|
+
* 1. Import model and utilities:
|
|
1590
|
+
* import { User } from '../models/user.model';
|
|
1591
|
+
${errorHandling ? "* import { AppError } from '../utils/AppError';" : ''}
|
|
1592
|
+
*
|
|
1593
|
+
* 2. Create service class or functions:
|
|
1594
|
+
* export class UserService {
|
|
1595
|
+
* static async getAllUsers() {
|
|
1596
|
+
${database === 'mongodb' ? '* return await User.find().select(\'-password\');' : database === 'postgresql' || database === 'mysql' ? '* return await User.findAll({ attributes: { exclude: [\'password\'] } });' : '* // Database query here'}
|
|
1597
|
+
* }
|
|
1598
|
+
*
|
|
1599
|
+
* static async getUserById(id: string) {
|
|
1600
|
+
${database === 'mongodb' ? '* const user = await User.findById(id).select(\'-password\');' : database === 'postgresql' || database === 'mysql' ? '* const user = await User.findByPk(id);' : '* // Database query here'}
|
|
1601
|
+
${errorHandling ? "* if (!user) throw new AppError('User not found', 404);" : "* if (!user) throw new Error('User not found');"}
|
|
1602
|
+
* return user;
|
|
1603
|
+
* }
|
|
1604
|
+
*
|
|
1605
|
+
* static async createUser(data: any) {
|
|
1606
|
+
${database === 'mongodb' ? '* return await User.create(data);' : database === 'postgresql' || database === 'mysql' ? '* return await User.create(data);' : '* // Create record'}
|
|
1607
|
+
* }
|
|
1608
|
+
*
|
|
1609
|
+
* static async updateUser(id: string, data: any) {
|
|
1610
|
+
${database === 'mongodb' ? '* const user = await User.findByIdAndUpdate(id, data, { new: true });' : database === 'postgresql' || database === 'mysql' ? '* await User.update(data, { where: { id } });\n* return await User.findByPk(id);' : '* // Update record'}
|
|
1611
|
+
${errorHandling ? "* if (!user) throw new AppError('User not found', 404);" : "* if (!user) throw new Error('User not found');"}
|
|
1612
|
+
* return user;
|
|
1613
|
+
* }
|
|
1614
|
+
*
|
|
1615
|
+
* static async deleteUser(id: string) {
|
|
1616
|
+
${database === 'mongodb' ? '* const user = await User.findByIdAndDelete(id);' : database === 'postgresql' || database === 'mysql' ? '* const deleted = await User.destroy({ where: { id } });' : '* // Delete record'}
|
|
1617
|
+
${errorHandling ? "* if (!user) throw new AppError('User not found', 404);" : "* if (!user) throw new Error('User not found');"}
|
|
1618
|
+
* return user;
|
|
1619
|
+
* }
|
|
1620
|
+
* }
|
|
1621
|
+
*
|
|
1622
|
+
* Service best practices:
|
|
1623
|
+
* - Keep business logic out of controllers
|
|
1624
|
+
* - Make services reusable
|
|
1625
|
+
* - Throw meaningful errors
|
|
1626
|
+
* - Use transactions for multiple database operations
|
|
1627
|
+
* - Keep services focused (Single Responsibility)
|
|
1628
|
+
*
|
|
1629
|
+
* Common service files:
|
|
1630
|
+
* - user.service.ts - User business logic
|
|
1631
|
+
* - auth.service.ts - Authentication logic
|
|
1632
|
+
* - email.service.ts - Email sending logic
|
|
1633
|
+
*/
|
|
1634
|
+
|
|
1635
|
+
// Example: Uncomment to use
|
|
1636
|
+
// export class ExampleService {
|
|
1637
|
+
// static async exampleMethod() {
|
|
1638
|
+
// // Business logic here
|
|
1639
|
+
// }
|
|
1640
|
+
// }`
|
|
1641
|
+
: `${database === 'mongodb' ? "// const User = require('../models/user.model');" : database === 'postgresql' || database === 'mysql' ? "// const { User } = require('../models/user.model');" : ''}
|
|
1642
|
+
${errorHandling ? "const { AppError } = require('../utils/AppError');" : ''}
|
|
1643
|
+
|
|
1644
|
+
/**
|
|
1645
|
+
* SERVICES FOLDER
|
|
1646
|
+
*
|
|
1647
|
+
* This folder contains business logic and data access layer.
|
|
1648
|
+
*
|
|
1649
|
+
* Purpose:
|
|
1650
|
+
* - Encapsulate business logic
|
|
1651
|
+
* - Interact with database models
|
|
1652
|
+
* - Handle data transformations
|
|
1653
|
+
* - Reusable functions across controllers
|
|
1654
|
+
*
|
|
1655
|
+
* How to create a service:
|
|
1656
|
+
*
|
|
1657
|
+
* 1. Import model and utilities:
|
|
1658
|
+
* const User = require('../models/user.model');
|
|
1659
|
+
${errorHandling ? "* const { AppError } = require('../utils/AppError');" : ''}
|
|
1660
|
+
*
|
|
1661
|
+
* 2. Create service functions:
|
|
1662
|
+
* class UserService {
|
|
1663
|
+
* static async getAllUsers() {
|
|
1664
|
+
${database === 'mongodb' ? '* return await User.find().select(\'-password\');' : database === 'postgresql' || database === 'mysql' ? '* return await User.findAll({ attributes: { exclude: [\'password\'] } });' : '* // Database query here'}
|
|
1665
|
+
* }
|
|
1666
|
+
*
|
|
1667
|
+
* static async getUserById(id) {
|
|
1668
|
+
${database === 'mongodb' ? '* const user = await User.findById(id).select(\'-password\');' : database === 'postgresql' || database === 'mysql' ? '* const user = await User.findByPk(id);' : '* // Database query here'}
|
|
1669
|
+
${errorHandling ? "* if (!user) throw new AppError('User not found', 404);" : "* if (!user) throw new Error('User not found');"}
|
|
1670
|
+
* return user;
|
|
1671
|
+
* }
|
|
1672
|
+
*
|
|
1673
|
+
* static async createUser(data) {
|
|
1674
|
+
${database === 'mongodb' ? '* return await User.create(data);' : database === 'postgresql' || database === 'mysql' ? '* return await User.create(data);' : '* // Create record'}
|
|
1675
|
+
* }
|
|
1676
|
+
*
|
|
1677
|
+
* static async updateUser(id, data) {
|
|
1678
|
+
${database === 'mongodb' ? '* const user = await User.findByIdAndUpdate(id, data, { new: true });' : database === 'postgresql' || database === 'mysql' ? '* await User.update(data, { where: { id } });\n* return await User.findByPk(id);' : '* // Update record'}
|
|
1679
|
+
${errorHandling ? "* if (!user) throw new AppError('User not found', 404);" : "* if (!user) throw new Error('User not found');"}
|
|
1680
|
+
* return user;
|
|
1681
|
+
* }
|
|
1682
|
+
*
|
|
1683
|
+
* static async deleteUser(id) {
|
|
1684
|
+
${database === 'mongodb' ? '* const user = await User.findByIdAndDelete(id);' : database === 'postgresql' || database === 'mysql' ? '* const deleted = await User.destroy({ where: { id } });' : '* // Delete record'}
|
|
1685
|
+
${errorHandling ? "* if (!user) throw new AppError('User not found', 404);" : "* if (!user) throw new Error('User not found');"}
|
|
1686
|
+
* return user;
|
|
1687
|
+
* }
|
|
1688
|
+
* }
|
|
1689
|
+
*
|
|
1690
|
+
* 3. Export service:
|
|
1691
|
+
* module.exports = UserService;
|
|
1692
|
+
*
|
|
1693
|
+
* Service best practices:
|
|
1694
|
+
* - Keep business logic out of controllers
|
|
1695
|
+
* - Make services reusable
|
|
1696
|
+
* - Throw meaningful errors
|
|
1697
|
+
* - Use transactions for multiple database operations
|
|
1698
|
+
* - Keep services focused (Single Responsibility)
|
|
1699
|
+
*
|
|
1700
|
+
* Common service files:
|
|
1701
|
+
* - user.service.js - User business logic
|
|
1702
|
+
* - auth.service.js - Authentication logic
|
|
1703
|
+
* - email.service.js - Email sending logic
|
|
1704
|
+
*/
|
|
1705
|
+
|
|
1706
|
+
// Example: Uncomment to use
|
|
1707
|
+
// class ExampleService {
|
|
1708
|
+
// static async exampleMethod() {
|
|
1709
|
+
// // Business logic here
|
|
1710
|
+
// }
|
|
1711
|
+
// }
|
|
1712
|
+
//
|
|
1713
|
+
// module.exports = ExampleService;`
|
|
1714
|
+
);
|
|
1715
|
+
|
|
1716
|
+
// Middlewares info
|
|
1717
|
+
writeFile(path.join(projectPath, `src/middlewares/info.${ext}`), isTS
|
|
1718
|
+
? `import { Request, Response, NextFunction } from 'express';
|
|
1719
|
+
${errorHandling ? "import { AppError } from '../utils/AppError';\nimport { errorResponse } from '../utils/response';" : ''}
|
|
1720
|
+
|
|
1721
|
+
/**
|
|
1722
|
+
* MIDDLEWARES FOLDER
|
|
1723
|
+
*
|
|
1724
|
+
* This folder contains Express middleware functions.
|
|
1725
|
+
*
|
|
1726
|
+
* Purpose:
|
|
1727
|
+
* - Intercept and process requests before reaching controllers
|
|
1728
|
+
* - Authentication and authorization
|
|
1729
|
+
* - Request validation
|
|
1730
|
+
* - Error handling
|
|
1731
|
+
* - Logging and monitoring
|
|
1732
|
+
*
|
|
1733
|
+
* How to create middleware:
|
|
1734
|
+
*
|
|
1735
|
+
* 1. Basic middleware:
|
|
1736
|
+
* export const logRequest = (
|
|
1737
|
+
* req: Request,
|
|
1738
|
+
* res: Response,
|
|
1739
|
+
* next: NextFunction
|
|
1740
|
+
* ) => {
|
|
1741
|
+
* console.log(\`\${req.method} \${req.path}\`);
|
|
1742
|
+
* next();
|
|
1743
|
+
* };
|
|
1744
|
+
*
|
|
1745
|
+
* 2. Authentication middleware:
|
|
1746
|
+
* import jwt from 'jsonwebtoken';
|
|
1747
|
+
*
|
|
1748
|
+
* export const authenticate = (
|
|
1749
|
+
* req: Request,
|
|
1750
|
+
* res: Response,
|
|
1751
|
+
* next: NextFunction
|
|
1752
|
+
* ) => {
|
|
1753
|
+
* try {
|
|
1754
|
+
* const token = req.headers.authorization?.split(' ')[1];
|
|
1755
|
+
${errorHandling ? "* if (!token) throw new AppError('No token provided', 401);" : "* if (!token) return res.status(401).json({ error: 'No token' });"}
|
|
1756
|
+
*
|
|
1757
|
+
* const decoded = jwt.verify(token, process.env.JWT_SECRET!);
|
|
1758
|
+
* req.user = decoded;
|
|
1759
|
+
* next();
|
|
1760
|
+
* } catch (error) {
|
|
1761
|
+
* next(error);
|
|
1762
|
+
* }
|
|
1763
|
+
* };
|
|
1764
|
+
*
|
|
1765
|
+
* 3. Validation middleware:
|
|
1766
|
+
* import { z } from 'zod';
|
|
1767
|
+
*
|
|
1768
|
+
* export const validateRequest = (schema: z.ZodSchema) => {
|
|
1769
|
+
* return (req: Request, res: Response, next: NextFunction) => {
|
|
1770
|
+
* const result = schema.safeParse(req.body);
|
|
1771
|
+
* if (!result.success) {
|
|
1772
|
+
${errorHandling ? "* return errorResponse(res, 'Validation failed', 400, result.error.errors);" : "* return res.status(400).json({ errors: result.error.errors });"}
|
|
1773
|
+
* }
|
|
1774
|
+
* req.body = result.data;
|
|
1775
|
+
* next();
|
|
1776
|
+
* };
|
|
1777
|
+
* };
|
|
1778
|
+
*
|
|
1779
|
+
* 4. Error handling middleware:
|
|
1780
|
+
* export const errorHandler = (
|
|
1781
|
+
* err: any,
|
|
1782
|
+
* req: Request,
|
|
1783
|
+
* res: Response,
|
|
1784
|
+
* next: NextFunction
|
|
1785
|
+
* ) => {
|
|
1786
|
+
* const statusCode = err.statusCode || 500;
|
|
1787
|
+
* res.status(statusCode).json({
|
|
1788
|
+
* success: false,
|
|
1789
|
+
* message: err.message,
|
|
1790
|
+
* ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
|
|
1791
|
+
* });
|
|
1792
|
+
* };
|
|
1793
|
+
*
|
|
1794
|
+
* Common middleware files:
|
|
1795
|
+
* - auth.middleware.ts - Authentication/authorization
|
|
1796
|
+
* - validation.middleware.ts - Request validation
|
|
1797
|
+
* - upload.middleware.ts - File uploads
|
|
1798
|
+
* - rateLimit.middleware.ts - Rate limiting
|
|
1799
|
+
*/
|
|
1800
|
+
|
|
1801
|
+
// Example: Uncomment to use
|
|
1802
|
+
// export const exampleMiddleware = (
|
|
1803
|
+
// req: Request,
|
|
1804
|
+
// res: Response,
|
|
1805
|
+
// next: NextFunction
|
|
1806
|
+
// ) => {
|
|
1807
|
+
// // Middleware logic here
|
|
1808
|
+
// next();
|
|
1809
|
+
// };`
|
|
1810
|
+
: `${errorHandling ? "const { AppError } = require('../utils/AppError');\nconst { errorResponse } = require('../utils/response');" : ''}
|
|
1811
|
+
|
|
1812
|
+
/**
|
|
1813
|
+
* MIDDLEWARES FOLDER
|
|
1814
|
+
*
|
|
1815
|
+
* This folder contains Express middleware functions.
|
|
1816
|
+
*
|
|
1817
|
+
* Purpose:
|
|
1818
|
+
* - Intercept and process requests before reaching controllers
|
|
1819
|
+
* - Authentication and authorization
|
|
1820
|
+
* - Request validation
|
|
1821
|
+
* - Error handling
|
|
1822
|
+
* - Logging and monitoring
|
|
1823
|
+
*
|
|
1824
|
+
* How to create middleware:
|
|
1825
|
+
*
|
|
1826
|
+
* 1. Basic middleware:
|
|
1827
|
+
* const logRequest = (req, res, next) => {
|
|
1828
|
+
* console.log(\`\${req.method} \${req.path}\`);
|
|
1829
|
+
* next();
|
|
1830
|
+
* };
|
|
1831
|
+
*
|
|
1832
|
+
* 2. Authentication middleware:
|
|
1833
|
+
* const jwt = require('jsonwebtoken');
|
|
1834
|
+
*
|
|
1835
|
+
* const authenticate = (req, res, next) => {
|
|
1836
|
+
* try {
|
|
1837
|
+
* const token = req.headers.authorization?.split(' ')[1];
|
|
1838
|
+
${errorHandling ? "* if (!token) throw new AppError('No token provided', 401);" : "* if (!token) return res.status(401).json({ error: 'No token' });"}
|
|
1839
|
+
*
|
|
1840
|
+
* const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
1841
|
+
* req.user = decoded;
|
|
1842
|
+
* next();
|
|
1843
|
+
* } catch (error) {
|
|
1844
|
+
* next(error);
|
|
1845
|
+
* }
|
|
1846
|
+
* };
|
|
1847
|
+
*
|
|
1848
|
+
* 3. Validation middleware:
|
|
1849
|
+
* const { z } = require('zod');
|
|
1850
|
+
*
|
|
1851
|
+
* const validateRequest = (schema) => {
|
|
1852
|
+
* return (req, res, next) => {
|
|
1853
|
+
* const result = schema.safeParse(req.body);
|
|
1854
|
+
* if (!result.success) {
|
|
1855
|
+
${errorHandling ? "* return errorResponse(res, 'Validation failed', 400, result.error.errors);" : "* return res.status(400).json({ errors: result.error.errors });"}
|
|
1856
|
+
* }
|
|
1857
|
+
* req.body = result.data;
|
|
1858
|
+
* next();
|
|
1859
|
+
* };
|
|
1860
|
+
* };
|
|
1861
|
+
*
|
|
1862
|
+
* 4. Error handling middleware:
|
|
1863
|
+
* const errorHandler = (err, req, res, next) => {
|
|
1864
|
+
* const statusCode = err.statusCode || 500;
|
|
1865
|
+
* res.status(statusCode).json({
|
|
1866
|
+
* success: false,
|
|
1867
|
+
* message: err.message,
|
|
1868
|
+
* ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
|
|
1869
|
+
* });
|
|
1870
|
+
* };
|
|
1871
|
+
*
|
|
1872
|
+
* 5. Export middleware:
|
|
1873
|
+
* module.exports = { logRequest, authenticate, validateRequest };
|
|
1874
|
+
*
|
|
1875
|
+
* Common middleware files:
|
|
1876
|
+
* - auth.middleware.js - Authentication/authorization
|
|
1877
|
+
* - validation.middleware.js - Request validation
|
|
1878
|
+
* - upload.middleware.js - File uploads
|
|
1879
|
+
* - rateLimit.middleware.js - Rate limiting
|
|
1880
|
+
*/
|
|
1881
|
+
|
|
1882
|
+
// Example: Uncomment to use
|
|
1883
|
+
// const exampleMiddleware = (req, res, next) => {
|
|
1884
|
+
// // Middleware logic here
|
|
1885
|
+
// next();
|
|
1886
|
+
// };
|
|
1887
|
+
//
|
|
1888
|
+
// module.exports = { exampleMiddleware };`
|
|
1889
|
+
);
|
|
1890
|
+
|
|
1891
|
+
// ============================================================
|
|
1892
|
+
// 14. DOCKER FILES (if enabled)
|
|
1893
|
+
// ============================================================
|
|
1894
|
+
if (docker) {
|
|
1895
|
+
writeFile(path.join(projectPath, 'Dockerfile'), `FROM node:18-alpine
|
|
1896
|
+
|
|
1897
|
+
WORKDIR /app
|
|
1898
|
+
|
|
1899
|
+
COPY package*.json ./
|
|
1900
|
+
RUN npm ci --only=production
|
|
1901
|
+
|
|
1902
|
+
COPY . .
|
|
1903
|
+
${isTS ? 'RUN npm run build\n' : ''}
|
|
1904
|
+
EXPOSE 5000
|
|
1905
|
+
|
|
1906
|
+
CMD ["npm", "start"]
|
|
1907
|
+
`);
|
|
1908
|
+
|
|
1909
|
+
let composeContent = `version: '3.8'
|
|
1910
|
+
|
|
1911
|
+
services:
|
|
1912
|
+
app:
|
|
1913
|
+
build: .
|
|
1914
|
+
ports:
|
|
1915
|
+
- "\${PORT:-5000}:5000"
|
|
1916
|
+
env_file:
|
|
1917
|
+
- .env
|
|
1918
|
+
restart: unless-stopped
|
|
1919
|
+
`;
|
|
1920
|
+
|
|
1921
|
+
if (database === 'mongodb') {
|
|
1922
|
+
composeContent += ` depends_on:
|
|
1923
|
+
- mongodb
|
|
1924
|
+
|
|
1925
|
+
mongodb:
|
|
1926
|
+
image: mongo:7
|
|
1927
|
+
ports:
|
|
1928
|
+
- "27017:27017"
|
|
1929
|
+
volumes:
|
|
1930
|
+
- mongodb_data:/data/db
|
|
1931
|
+
restart: unless-stopped
|
|
1932
|
+
|
|
1933
|
+
volumes:
|
|
1934
|
+
mongodb_data:
|
|
1935
|
+
`;
|
|
1936
|
+
} else if (database === 'postgresql') {
|
|
1937
|
+
composeContent += ` depends_on:
|
|
1938
|
+
- postgres
|
|
1939
|
+
|
|
1940
|
+
postgres:
|
|
1941
|
+
image: postgres:16
|
|
1942
|
+
environment:
|
|
1943
|
+
POSTGRES_DB: \${DB_NAME:-my-back}
|
|
1944
|
+
POSTGRES_USER: \${DB_USER:-postgres}
|
|
1945
|
+
POSTGRES_PASSWORD: \${DB_PASSWORD:-root}
|
|
1946
|
+
ports:
|
|
1947
|
+
- "\${DB_PORT:-5432}:5432"
|
|
1948
|
+
volumes:
|
|
1949
|
+
- postgres_data:/var/lib/postgresql/data
|
|
1950
|
+
restart: unless-stopped
|
|
1951
|
+
|
|
1952
|
+
volumes:
|
|
1953
|
+
postgres_data:
|
|
1954
|
+
`;
|
|
1955
|
+
} else if (database === 'mysql') {
|
|
1956
|
+
composeContent += ` depends_on:
|
|
1957
|
+
- mysql
|
|
1958
|
+
|
|
1959
|
+
mysql:
|
|
1960
|
+
image: mysql:8
|
|
1961
|
+
environment:
|
|
1962
|
+
MYSQL_DATABASE: \${DB_NAME:-${projectName}}
|
|
1963
|
+
MYSQL_USER: \${DB_USER:-user}
|
|
1964
|
+
MYSQL_PASSWORD: \${DB_PASSWORD:-password}
|
|
1965
|
+
MYSQL_ROOT_PASSWORD: \${DB_PASSWORD:-password}
|
|
1966
|
+
ports:
|
|
1967
|
+
- "\${DB_PORT:-3306}:3306"
|
|
1968
|
+
volumes:
|
|
1969
|
+
- mysql_data:/var/lib/mysql
|
|
1970
|
+
restart: unless-stopped
|
|
1971
|
+
|
|
1972
|
+
volumes:
|
|
1973
|
+
mysql_data:
|
|
1974
|
+
`;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
writeFile(path.join(projectPath, 'docker-compose.yml'), composeContent);
|
|
1978
|
+
|
|
1979
|
+
writeFile(path.join(projectPath, '.dockerignore'), `node_modules
|
|
1980
|
+
npm-debug.log
|
|
1981
|
+
.env
|
|
1982
|
+
.git
|
|
1983
|
+
.gitignore
|
|
1984
|
+
README.md
|
|
1985
|
+
${isTS ? 'dist\n' : ''}logs
|
|
1986
|
+
*.log
|
|
1987
|
+
`);
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
// ============================================================
|
|
1991
|
+
// 15. README.md
|
|
1992
|
+
// ============================================================
|
|
1993
|
+
const readmeContent = `# ${projectName}
|
|
1994
|
+
|
|
1995
|
+
Backend API built with Backend Forge v2.0
|
|
1996
|
+
|
|
1997
|
+
## Features
|
|
1998
|
+
|
|
1999
|
+
- ✅ ${language}
|
|
2000
|
+
- ✅ ${database === 'mongodb' ? 'MongoDB + Mongoose' : database === 'postgresql' ? 'PostgreSQL + Sequelize' : database === 'mysql' ? 'MySQL + Sequelize' : 'No Database'}
|
|
2001
|
+
${auth ? '- ✅ JWT Authentication' : ''}
|
|
2002
|
+
${validation !== 'none' ? `- ✅ ${validation === 'zod' ? 'Zod' : 'Joi'} Validation` : ''}
|
|
2003
|
+
${logger !== 'None' ? `- ✅ ${logger} Logger` : ''}
|
|
2004
|
+
${errorHandling ? '- ✅ Custom Error Handling & Response Utilities' : ''}
|
|
2005
|
+
${docker ? '- ✅ Docker Support' : ''}
|
|
2006
|
+
- ✅ Environment Configuration
|
|
2007
|
+
- ✅ CORS & Security (Helmet)
|
|
2008
|
+
|
|
2009
|
+
## Setup
|
|
2010
|
+
|
|
2011
|
+
\`\`\`bash
|
|
2012
|
+
# Install dependencies
|
|
2013
|
+
npm install
|
|
2014
|
+
|
|
2015
|
+
# Configure environment
|
|
2016
|
+
cp .env.example .env
|
|
2017
|
+
# Edit .env with your settings
|
|
2018
|
+
|
|
2019
|
+
${database === 'postgresql' || database === 'mysql' ? `# Run database migrations
|
|
2020
|
+
npx sequelize-cli db:migrate
|
|
2021
|
+
|
|
2022
|
+
` : ''}# Start development server
|
|
2023
|
+
npm run dev
|
|
2024
|
+
\`\`\`
|
|
2025
|
+
|
|
2026
|
+
## Project Structure
|
|
2027
|
+
|
|
2028
|
+
\`\`\`
|
|
2029
|
+
src/
|
|
2030
|
+
├── config/ # Configuration files
|
|
2031
|
+
│ ├── env.${ext} # Environment constants
|
|
2032
|
+
│ ├── database.${ext} # Database connection
|
|
2033
|
+
${logger !== 'None' ? `│ └── logger.${ext} # Logger setup\n` : ''}├── routes/ # API routes
|
|
2034
|
+
├── controllers/ # Route controllers
|
|
2035
|
+
├── services/ # Business logic
|
|
2036
|
+
├── models/ # Database models
|
|
2037
|
+
${validation !== 'none' ? `├── validators/ # Request validators\n` : ''}├── middlewares/ # Custom middleware
|
|
2038
|
+
├── utils/ # Utility functions
|
|
2039
|
+
${errorHandling ? `│ ├── AppError.${ext} # Custom error class
|
|
2040
|
+
│ └── response.${ext} # Response helpers
|
|
2041
|
+
` : ''}├── app.${ext} # Express app
|
|
2042
|
+
└── server.${ext} # Server entry point
|
|
2043
|
+
\`\`\`
|
|
2044
|
+
|
|
2045
|
+
## Available Scripts
|
|
2046
|
+
|
|
2047
|
+
- \`npm run dev\` - Start development server
|
|
2048
|
+
- \`npm start\` - Start production server
|
|
2049
|
+
${isTS ? '- `npm run build` - Build TypeScript\n' : ''}${database === 'postgresql' || database === 'mysql' ? `- \`npm run db:migrate\` - Run database migrations
|
|
2050
|
+
- \`npm run db:migrate:undo\` - Undo last migration
|
|
2051
|
+
- \`npm run db:seed\` - Run database seeders
|
|
2052
|
+
` : ''}
|
|
2053
|
+
## API Endpoints
|
|
2054
|
+
|
|
2055
|
+
### Health Check
|
|
2056
|
+
\`\`\`
|
|
2057
|
+
GET /
|
|
2058
|
+
\`\`\`
|
|
2059
|
+
|
|
2060
|
+
Add your routes in \`src/routes/\`
|
|
2061
|
+
|
|
2062
|
+
${errorHandling ? `## Error Handling
|
|
2063
|
+
|
|
2064
|
+
The project includes:
|
|
2065
|
+
- \`AppError\` class for consistent error handling
|
|
2066
|
+
- \`successResponse\` helper for success responses
|
|
2067
|
+
- \`errorResponse\` helper for error responses
|
|
2068
|
+
|
|
2069
|
+
Example usage:
|
|
2070
|
+
|
|
2071
|
+
\`\`\`${ext}
|
|
2072
|
+
import { AppError } from './utils/AppError';
|
|
2073
|
+
import { successResponse, errorResponse } from './utils/response';
|
|
2074
|
+
|
|
2075
|
+
// Throw custom error
|
|
2076
|
+
throw new AppError('User not found', 404);
|
|
2077
|
+
|
|
2078
|
+
// Success response
|
|
2079
|
+
successResponse(res, data, 'User created', 201);
|
|
2080
|
+
|
|
2081
|
+
// Error response
|
|
2082
|
+
errorResponse(res, 'Invalid input', 400);
|
|
2083
|
+
\`\`\`
|
|
2084
|
+
|
|
2085
|
+
` : ''}${database === 'postgresql' || database === 'mysql' ? `## Database Migrations
|
|
2086
|
+
|
|
2087
|
+
Create a new migration:
|
|
2088
|
+
|
|
2089
|
+
\`\`\`bash
|
|
2090
|
+
npx sequelize-cli migration:generate --name create-users-table
|
|
2091
|
+
\`\`\`
|
|
2092
|
+
|
|
2093
|
+
Run migrations:
|
|
2094
|
+
|
|
2095
|
+
\`\`\`bash
|
|
2096
|
+
npm run db:migrate
|
|
2097
|
+
\`\`\`
|
|
2098
|
+
|
|
2099
|
+
` : ''}${docker ? `## Docker
|
|
2100
|
+
|
|
2101
|
+
\`\`\`bash
|
|
2102
|
+
# Start all services
|
|
2103
|
+
docker-compose up
|
|
2104
|
+
|
|
2105
|
+
# Start in background
|
|
2106
|
+
docker-compose up -d
|
|
2107
|
+
|
|
2108
|
+
# Stop services
|
|
2109
|
+
docker-compose down
|
|
2110
|
+
\`\`\`
|
|
2111
|
+
|
|
2112
|
+
` : ''}## Environment Variables
|
|
2113
|
+
|
|
2114
|
+
See \`.env.example\` for all available environment variables.
|
|
2115
|
+
|
|
2116
|
+
## License
|
|
2117
|
+
|
|
2118
|
+
MIT
|
|
2119
|
+
`;
|
|
2120
|
+
|
|
2121
|
+
writeFile(path.join(projectPath, 'README.md'), readmeContent);
|
|
2122
|
+
|
|
2123
|
+
// ============================================================
|
|
2124
|
+
// 16. TYPES (if TypeScript)
|
|
2125
|
+
// ============================================================
|
|
2126
|
+
if (isTS) {
|
|
2127
|
+
writeFile(path.join(projectPath, 'src/types/index.ts'), `// Add your TypeScript types and interfaces here
|
|
2128
|
+
|
|
2129
|
+
export interface ApiResponse<T = any> {
|
|
2130
|
+
success: boolean;
|
|
2131
|
+
message: string;
|
|
2132
|
+
data?: T;
|
|
2133
|
+
}
|
|
2134
|
+
`);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
module.exports = { createProject };
|