backend-scaffold-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +439 -0
- package/package.json +32 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const { program } = require('commander');
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
const inquirer = require('inquirer');
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { execSync } = require('child_process');
|
|
10
|
+
|
|
11
|
+
const version= '1.0.0';
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.version(version)
|
|
15
|
+
.description('Create a new Express.js backend application')
|
|
16
|
+
.argument('<project-name>', 'Name of the project')
|
|
17
|
+
.action(async (projectName) => {
|
|
18
|
+
try{
|
|
19
|
+
console.log(chalk.blue(`Creating a new Express.js backend application: ${projectName}`));
|
|
20
|
+
|
|
21
|
+
const answers = await inquirer.prompt([
|
|
22
|
+
{
|
|
23
|
+
type: 'confirm',
|
|
24
|
+
name: 'useAuth',
|
|
25
|
+
message: 'Include JWT authentication?',
|
|
26
|
+
default: true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type:'confirm',
|
|
30
|
+
name: 'useLogging',
|
|
31
|
+
message: 'Include logging middleware?',
|
|
32
|
+
default: true
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
type: 'confirm',
|
|
36
|
+
name: 'useValidation',
|
|
37
|
+
message: 'Include input validation?',
|
|
38
|
+
default: true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: 'confirm',
|
|
42
|
+
name: 'installDeps',
|
|
43
|
+
message: 'Install dependencies now?',
|
|
44
|
+
default: true
|
|
45
|
+
}
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
const projectPath = path.resolve(process.cwd(), projectName);
|
|
49
|
+
|
|
50
|
+
if (fs.existsSync(projectPath)) {
|
|
51
|
+
console.log(chalk.red(`Error: Directory ${projectName} already exists.`));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log(chalk.blue(`Creating project in ${projectPath}\n`));
|
|
56
|
+
fs.ensureDirSync(projectPath);
|
|
57
|
+
|
|
58
|
+
createFolderStructure(projectPath);
|
|
59
|
+
createConfigFiles(projectPath, answers);
|
|
60
|
+
createMiddlewareFiles(projectPath, answers);
|
|
61
|
+
createRouteFiles(projectPath, answers);
|
|
62
|
+
|
|
63
|
+
console.log(chalk.cyan(' Initializing git repository...'));
|
|
64
|
+
execSync('git init', { cwd: projectPath });
|
|
65
|
+
|
|
66
|
+
if (answers.installDeps) {
|
|
67
|
+
console.log(chalk.cyan(' Installing dependencies...\n'));
|
|
68
|
+
execSync('npm install', { cwd: projectPath, stdio: 'inherit' });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(chalk.green.bold('\n Project created successfully!\n'));
|
|
72
|
+
console.log(chalk.cyan('Next steps:'));
|
|
73
|
+
console.log(chalk.white(` cd ${projectName}`));
|
|
74
|
+
console.log(chalk.white(' cp .env.example .env'));
|
|
75
|
+
console.log(chalk.white(' npm start\n'));
|
|
76
|
+
}catch (error) {
|
|
77
|
+
console.error(chalk.red('\n Error creating project:'), error.message);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
program.parse();
|
|
83
|
+
|
|
84
|
+
const createFolderStructure=(projectPath) =>{
|
|
85
|
+
const folders=[
|
|
86
|
+
'src/config',
|
|
87
|
+
'src/middleware',
|
|
88
|
+
'src/routes',
|
|
89
|
+
'src/controllers',
|
|
90
|
+
'src/models',
|
|
91
|
+
'src/utils',
|
|
92
|
+
'src/validation'
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
folders.forEach(folder => {
|
|
96
|
+
fs.ensureDirSync(path.join(projectPath, folder));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
console.log(chalk.green(' Folder structure created'));
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
const createConfigFiles= (projectPath,answers) =>{
|
|
105
|
+
const packageJson={
|
|
106
|
+
name: path.basename(projectPath),
|
|
107
|
+
version: '1.0.0',
|
|
108
|
+
main: 'src/server.js',
|
|
109
|
+
scripts: {
|
|
110
|
+
start: 'node src/server.js',
|
|
111
|
+
dev: 'nodemon src/server.js',
|
|
112
|
+
test: 'jest'
|
|
113
|
+
},
|
|
114
|
+
keywords: ['express', 'nodejs', 'backend'],
|
|
115
|
+
author: '',
|
|
116
|
+
license: 'MIT',
|
|
117
|
+
dependencies: {
|
|
118
|
+
express: '^4.18.2',
|
|
119
|
+
mongoose: '^7.0.0',
|
|
120
|
+
dotenv: '^16.0.3',
|
|
121
|
+
cors: '^2.8.5',
|
|
122
|
+
...(answers.useAuth && { jsonwebtoken: '^9.0.0' }),
|
|
123
|
+
...(answers.useValidation && { joi: '^17.9.0' })
|
|
124
|
+
},
|
|
125
|
+
devDependencies: {
|
|
126
|
+
nodemon: '^2.0.22',
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
fs.writeFileSync(
|
|
132
|
+
path.join(projectPath, 'package.json'),
|
|
133
|
+
JSON.stringify(packageJson, null, 2)
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const env = `# Server Configuration
|
|
137
|
+
PORT=5000
|
|
138
|
+
NODE_ENV=development
|
|
139
|
+
|
|
140
|
+
# MongoDB Configuration
|
|
141
|
+
MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/myappdb
|
|
142
|
+
DB_NAME=myapp
|
|
143
|
+
|
|
144
|
+
# DNS Configuration
|
|
145
|
+
DNS_SERVERS=8.8.8.8,8.8.4.4
|
|
146
|
+
PRIMARY_DNS=8.8.8.8
|
|
147
|
+
SECONDARY_DNS=8.8.4.4
|
|
148
|
+
|
|
149
|
+
# JWT Configuration
|
|
150
|
+
JWT_SECRET=your-secret-key-change-this-in-production
|
|
151
|
+
JWT_EXPIRE=7d
|
|
152
|
+
|
|
153
|
+
# CORS
|
|
154
|
+
CORS_ORIGIN=http://localhost:3000
|
|
155
|
+
|
|
156
|
+
# Logging
|
|
157
|
+
LOG_LEVEL=debug
|
|
158
|
+
`;
|
|
159
|
+
|
|
160
|
+
fs.writeFileSync(path.join(projectPath, '.env.example'), env);
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
const gitignore = `node_modules/
|
|
164
|
+
.env
|
|
165
|
+
.env.local
|
|
166
|
+
.env.*.local
|
|
167
|
+
dist/
|
|
168
|
+
build/
|
|
169
|
+
.DS_Store
|
|
170
|
+
*.log
|
|
171
|
+
npm-debug.log*
|
|
172
|
+
yarn-debug.log*
|
|
173
|
+
.idea/
|
|
174
|
+
.vscode/
|
|
175
|
+
`;
|
|
176
|
+
|
|
177
|
+
fs.writeFileSync(path.join(projectPath, '.gitignore'), gitignore);
|
|
178
|
+
|
|
179
|
+
console.log(chalk.green(' Config files created'));
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
function createMiddlewareFiles(projectPath, answers) {
|
|
188
|
+
const errorHandler = `// Middleware for handling errors
|
|
189
|
+
class ErrorHandler extends Error {
|
|
190
|
+
constructor(message, statusCode) {
|
|
191
|
+
super(message);
|
|
192
|
+
this.statusCode = statusCode;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const errorMiddleware = (err, req, res, next) => {
|
|
197
|
+
err.statusCode = err.statusCode || 500;
|
|
198
|
+
err.message = err.message || 'Internal Server Error';
|
|
199
|
+
|
|
200
|
+
if (err.code === 11000) {
|
|
201
|
+
const message = \`Duplicate field value entered\`;
|
|
202
|
+
err = new ErrorHandler(message, 400);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (err.name === 'JsonWebTokenError') {
|
|
206
|
+
const message = \`JSON Web Token is invalid, try again\`;
|
|
207
|
+
err = new ErrorHandler(message, 400);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (err.name === 'TokenExpiredError') {
|
|
211
|
+
const message = \`JSON Web Token is expired, try again\`;
|
|
212
|
+
err = new ErrorHandler(message, 400);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
res.status(err.statusCode).json({
|
|
216
|
+
success: false,
|
|
217
|
+
message: err.message,
|
|
218
|
+
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
|
|
219
|
+
});
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
module.exports = { errorMiddleware, ErrorHandler };
|
|
223
|
+
`;
|
|
224
|
+
|
|
225
|
+
fs.writeFileSync(
|
|
226
|
+
path.join(projectPath, 'src/middleware/errorHandler.js'),
|
|
227
|
+
errorHandler
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
const logger = `// Simple logging middleware
|
|
232
|
+
const loggerMiddleware = (req, res, next) => {
|
|
233
|
+
const start = Date.now();
|
|
234
|
+
|
|
235
|
+
res.on('finish', () => {
|
|
236
|
+
const duration = Date.now() - start;
|
|
237
|
+
console.log(
|
|
238
|
+
\`[\${new Date().toISOString()}] \${req.method} \${req.path} - \${res.statusCode} - \${duration}ms\`
|
|
239
|
+
);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
next();
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
module.exports = loggerMiddleware;
|
|
246
|
+
`;
|
|
247
|
+
|
|
248
|
+
fs.writeFileSync(
|
|
249
|
+
path.join(projectPath, 'src/middleware/logger.js'),
|
|
250
|
+
logger
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
if (answers.useAuth) {
|
|
254
|
+
const auth = `const jwt = require('jsonwebtoken');
|
|
255
|
+
const { ErrorHandler } = require('./errorHandler');
|
|
256
|
+
|
|
257
|
+
const authenticateToken = (req, res, next) => {
|
|
258
|
+
const token = req.headers['authorization']?.split(' ')[1];
|
|
259
|
+
|
|
260
|
+
if (!token) {
|
|
261
|
+
return next(new ErrorHandler('Access token is missing', 401));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
266
|
+
req.user = decoded;
|
|
267
|
+
next();
|
|
268
|
+
} catch (err) {
|
|
269
|
+
next(new ErrorHandler('Invalid or expired token', 401));
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
module.exports = authenticateToken;
|
|
274
|
+
`;
|
|
275
|
+
|
|
276
|
+
fs.writeFileSync(
|
|
277
|
+
path.join(projectPath, 'src/middleware/auth.js'),
|
|
278
|
+
auth
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (answers.useValidation) {
|
|
283
|
+
const validation = `const Joi = require('joi');
|
|
284
|
+
|
|
285
|
+
const validateRequest = (schema) => {
|
|
286
|
+
return (req, res, next) => {
|
|
287
|
+
const { error, value } = schema.validate(req.body, {
|
|
288
|
+
abortEarly: false,
|
|
289
|
+
stripUnknown: true
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
if (error) {
|
|
293
|
+
const details = error.details.map(d => ({
|
|
294
|
+
field: d.path.join('.'),
|
|
295
|
+
message: d.message
|
|
296
|
+
}));
|
|
297
|
+
return res.status(400).json({ success: false, errors: details });
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
req.validatedBody = value;
|
|
301
|
+
next();
|
|
302
|
+
};
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
module.exports = validateRequest;
|
|
306
|
+
`;
|
|
307
|
+
|
|
308
|
+
fs.writeFileSync(
|
|
309
|
+
path.join(projectPath, 'src/middleware/validation.js'),
|
|
310
|
+
validation
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
console.log(chalk.green(' Middleware files created'));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
const createRouteFiles = (projectPath, answers) => {
|
|
319
|
+
const routes = `const express = require('express');
|
|
320
|
+
const router = express.Router();
|
|
321
|
+
${answers.useAuth ? "const authenticateToken = require('../middleware/auth');" : ''}
|
|
322
|
+
|
|
323
|
+
// Public routes
|
|
324
|
+
router.get('/health', (req, res) => {
|
|
325
|
+
res.json({ status: 'API is running' });
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
${answers.useAuth ? `
|
|
329
|
+
// Protected routes
|
|
330
|
+
router.get('/protected', authenticateToken, (req, res) => {
|
|
331
|
+
res.json({ message: 'This is a protected route', user: req.user });
|
|
332
|
+
});
|
|
333
|
+
` : ''}
|
|
334
|
+
|
|
335
|
+
module.exports = router;
|
|
336
|
+
`;
|
|
337
|
+
fs.writeFileSync(
|
|
338
|
+
path.join(projectPath, 'src/routes/index.js'),
|
|
339
|
+
routes
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
const dbConfig = `const mongoose = require('mongoose');
|
|
345
|
+
const dns = require('dns');
|
|
346
|
+
|
|
347
|
+
// Set DNS servers
|
|
348
|
+
const dnsServers = (process.env.DNS_SERVERS || '8.8.8.8,8.8.4.4').split(',');
|
|
349
|
+
dns.setServers(dnsServers);
|
|
350
|
+
|
|
351
|
+
const connectDB = async () => {
|
|
352
|
+
try {
|
|
353
|
+
console.log(\`📡 Using DNS servers: \${dnsServers.join(', ')}\`);
|
|
354
|
+
|
|
355
|
+
const conn = await mongoose.connect(process.env.MONGODB_URI, {
|
|
356
|
+
useNewUrlParser: true,
|
|
357
|
+
useUnifiedTopology: true,
|
|
358
|
+
serverSelectionTimeoutMS: 5000,
|
|
359
|
+
socketTimeoutMS: 45000,
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
console.log(\`MongoDB Connected: \${conn.connection.host}\`);
|
|
363
|
+
return conn;
|
|
364
|
+
} catch (error) {
|
|
365
|
+
console.error(' MongoDB Connection Error:', error.message);
|
|
366
|
+
|
|
367
|
+
if (process.env.NODE_ENV === 'production') {
|
|
368
|
+
process.exit(1);
|
|
369
|
+
} else {
|
|
370
|
+
console.warn('âš Warning: Running without MongoDB connection');
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
module.exports = connectDB;
|
|
376
|
+
`;
|
|
377
|
+
|
|
378
|
+
fs.writeFileSync(
|
|
379
|
+
path.join(projectPath, 'src/config/db.js'),
|
|
380
|
+
dbConfig
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
const server = `require('dotenv').config();
|
|
384
|
+
const express = require('express');
|
|
385
|
+
const cors = require('cors');
|
|
386
|
+
const connectDB = require('./config/db');
|
|
387
|
+
const routes = require('./routes');
|
|
388
|
+
const loggerMiddleware = require('./middleware/logger');
|
|
389
|
+
const { errorMiddleware } = require('./middleware/errorHandler');
|
|
390
|
+
|
|
391
|
+
const app = express();
|
|
392
|
+
const PORT = process.env.PORT || 5000;
|
|
393
|
+
|
|
394
|
+
// Connect to MongoDB
|
|
395
|
+
connectDB();
|
|
396
|
+
|
|
397
|
+
// Middleware
|
|
398
|
+
app.use(cors({
|
|
399
|
+
origin: process.env.CORS_ORIGIN || '*'
|
|
400
|
+
}));
|
|
401
|
+
|
|
402
|
+
app.use(express.json());
|
|
403
|
+
app.use(express.urlencoded({ extended: true }));
|
|
404
|
+
app.use(loggerMiddleware);
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
// Routes
|
|
408
|
+
app.use('/api', routes);
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
// Health check
|
|
412
|
+
app.get('/', (req, res) => {
|
|
413
|
+
res.json({ message: 'Express Backend is running' });
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
// Error handling middleware (must be last)
|
|
418
|
+
app.use(errorMiddleware);
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
// Start Server
|
|
422
|
+
app.listen(PORT, () => {
|
|
423
|
+
console.log(\` Server running on port \${PORT}\`);
|
|
424
|
+
console.log(\`Environment: \${process.env.NODE_ENV || 'development'}\`);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
process.on('unhandledRejection', (err) => {
|
|
428
|
+
console.error('Unhandled Rejection:', err);
|
|
429
|
+
process.exit(1);
|
|
430
|
+
});
|
|
431
|
+
`;
|
|
432
|
+
|
|
433
|
+
fs.writeFileSync(
|
|
434
|
+
path.join(projectPath, 'src/server.js'),
|
|
435
|
+
server
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
console.log(chalk.green('Route and config files created'));
|
|
439
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "backend-scaffold-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool to scaffold Express.js backend projects with MongoDB, middleware, and common setup",
|
|
5
|
+
"main": "bin/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-express-backend": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"express",
|
|
14
|
+
"nodejs",
|
|
15
|
+
"backend",
|
|
16
|
+
"scaffold",
|
|
17
|
+
"cli",
|
|
18
|
+
"mongodb"
|
|
19
|
+
],
|
|
20
|
+
"author": "abhi0605",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"preferGlobal": true,
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=14.0.0"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"chalk": "^4.1.2",
|
|
28
|
+
"commander": "^11.0.0",
|
|
29
|
+
"fs-extra": "^11.1.0",
|
|
30
|
+
"inquirer": "^8.2.5"
|
|
31
|
+
}
|
|
32
|
+
}
|