nodejs-quickstart-structure 1.3.2 → 1.3.5
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/index.js +1 -1
- package/lib/generator.js +26 -4
- package/package.json +39 -39
- package/templates/clean-architecture/js/src/index.js.ejs +3 -3
- package/templates/clean-architecture/js/src/infrastructure/log/logger.js +5 -5
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +2 -1
- package/templates/clean-architecture/js/src/interfaces/controllers/UserController.js +6 -1
- package/templates/clean-architecture/ts/src/index.ts.ejs +3 -3
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +5 -5
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts +3 -2
- package/templates/common/kafka/js/services/{kafkaService.js → kafkaService.js.ejs} +2 -1
- package/templates/common/kafka/ts/services/{kafkaService.ts → kafkaService.ts.ejs} +2 -1
- package/templates/mvc/js/src/controllers/userController.js.ejs +3 -1
- package/templates/mvc/js/src/utils/logger.js +5 -5
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +3 -0
- package/templates/mvc/ts/src/index.ts.ejs +3 -3
- package/templates/mvc/ts/src/utils/logger.ts +5 -5
- package/templates/mvc/js/src/controllers/userController.js +0 -14
- /package/templates/clean-architecture/ts/src/infrastructure/repositories/{UserRepository.ts.ejs → userRepository.ts.ejs} +0 -0
package/bin/index.js
CHANGED
|
@@ -50,7 +50,7 @@ program
|
|
|
50
50
|
await generateProject(answers);
|
|
51
51
|
|
|
52
52
|
console.log(chalk.green('\n✔ Project generated successfully!'));
|
|
53
|
-
console.log(chalk.cyan(`\nNext steps:\n cd ${answers.projectName}\n npm install\n docker-compose up\n-----------------------\nStart the app manually:\n npm install\n npm run dev`));
|
|
53
|
+
console.log(chalk.cyan(`\nNext steps:\n cd ${answers.projectName}\n npm install\n docker-compose up\n-----------------------\nStart the app manually:\n cd ${answers.projectName}\n npm install\n npm run dev`));
|
|
54
54
|
|
|
55
55
|
} catch (error) {
|
|
56
56
|
console.error(chalk.red('Error generating project:'), error);
|
package/lib/generator.js
CHANGED
|
@@ -126,6 +126,24 @@ export const generateProject = async (config) => {
|
|
|
126
126
|
const kafkaSource = path.join(templatesDir, 'common', 'kafka', langExt);
|
|
127
127
|
await fs.copy(kafkaSource, path.join(targetDir, 'src'));
|
|
128
128
|
|
|
129
|
+
// Render Kafka Service with dynamic logger path
|
|
130
|
+
const kafkaServiceFileName = `kafkaService.${langExt}`;
|
|
131
|
+
const kafkaServiceTemplate = path.join(targetDir, 'src', 'services', `${kafkaServiceFileName}.ejs`);
|
|
132
|
+
|
|
133
|
+
if (await fs.pathExists(kafkaServiceTemplate)) {
|
|
134
|
+
const loggerPath = architecture === 'Clean Architecture' ? '../log/logger' : '../utils/logger';
|
|
135
|
+
// Note: For MVC, it's relative to src/services (../utils/logger). Wait, ../utils/logger means src/utils/logger.
|
|
136
|
+
// src/services/kafka.ts -> ../utils/logger -> src/utils/logger. Correct.
|
|
137
|
+
// But wait, generated code for MVC usually puts it in src/services.
|
|
138
|
+
// Let's re-verify MVC structure for logger.
|
|
139
|
+
// In MVC, logger is usually in src/utils/logger.ts?
|
|
140
|
+
// Let's check where logger is copied for MVC.
|
|
141
|
+
|
|
142
|
+
const content = ejs.render(await fs.readFile(kafkaServiceTemplate, 'utf-8'), { loggerPath });
|
|
143
|
+
await fs.writeFile(path.join(targetDir, 'src', 'services', kafkaServiceFileName), content);
|
|
144
|
+
await fs.remove(kafkaServiceTemplate);
|
|
145
|
+
}
|
|
146
|
+
|
|
129
147
|
if (architecture === 'Clean Architecture') {
|
|
130
148
|
// Clean Architecture Restructuring
|
|
131
149
|
await fs.ensureDir(path.join(targetDir, 'src/infrastructure/messaging'));
|
|
@@ -240,8 +258,10 @@ export const generateProject = async (config) => {
|
|
|
240
258
|
// MVC TS
|
|
241
259
|
const swaggerMvcTs = path.join(targetDir, 'src', 'config', 'swagger.ts.ejs');
|
|
242
260
|
if (await fs.pathExists(swaggerMvcTs)) {
|
|
243
|
-
|
|
244
|
-
|
|
261
|
+
if (communication === 'REST APIs') {
|
|
262
|
+
const content = ejs.render(await fs.readFile(swaggerMvcTs, 'utf-8'), { communication });
|
|
263
|
+
await fs.writeFile(path.join(targetDir, 'src', 'config', 'swagger.ts'), content);
|
|
264
|
+
}
|
|
245
265
|
await fs.remove(swaggerMvcTs);
|
|
246
266
|
}
|
|
247
267
|
// Clean Architecture TS
|
|
@@ -249,8 +269,10 @@ export const generateProject = async (config) => {
|
|
|
249
269
|
// Note: In Clean Arch, it might be in src/infrastructure/webserver or src/config depending on refactor.
|
|
250
270
|
// Based on previous moves, we saw it in src/config for TS.
|
|
251
271
|
if (await fs.pathExists(swaggerCleanTs)) {
|
|
252
|
-
|
|
253
|
-
|
|
272
|
+
if (communication === 'REST APIs') {
|
|
273
|
+
const content = ejs.render(await fs.readFile(swaggerCleanTs, 'utf-8'), { communication });
|
|
274
|
+
await fs.writeFile(path.join(targetDir, 'src', 'config', 'swagger.ts'), content);
|
|
275
|
+
}
|
|
254
276
|
await fs.remove(swaggerCleanTs);
|
|
255
277
|
}
|
|
256
278
|
|
package/package.json
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "nodejs-quickstart-structure",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "A CLI to scaffold Node.js microservices with MVC or Clean Architecture",
|
|
6
|
-
"main": "bin/index.js",
|
|
7
|
-
"bin": {
|
|
8
|
-
"nodejs-quickstart": "./bin/index.js"
|
|
9
|
-
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"test": "echo \"Error: no test specified\" && exit 1",
|
|
12
|
-
"test:e2e": "npm run test:e2e:windows",
|
|
13
|
-
"test:e2e:windows": "node scripts/validate-windows.js",
|
|
14
|
-
"test:e2e:linux": "node scripts/validate-linux.js"
|
|
15
|
-
},
|
|
16
|
-
"keywords": [
|
|
17
|
-
"nodejs",
|
|
18
|
-
"cli",
|
|
19
|
-
"scaffold",
|
|
20
|
-
"mvc",
|
|
21
|
-
"clean-architecture"
|
|
22
|
-
],
|
|
23
|
-
"author": "Pau Dang <phucdangb1400718@gmail.com>",
|
|
24
|
-
"repository": {
|
|
25
|
-
"type": "git",
|
|
26
|
-
"url": "git+https://github.com/paudang/nodejs-quickstart-structure.git"
|
|
27
|
-
},
|
|
28
|
-
"bugs": {
|
|
29
|
-
"url": "https://github.com/paudang/nodejs-quickstart-structure/issues"
|
|
30
|
-
},
|
|
31
|
-
"homepage": "https://github.com/paudang/nodejs-quickstart-structure#readme",
|
|
32
|
-
"license": "ISC",
|
|
33
|
-
"dependencies": {
|
|
34
|
-
"chalk": "^5.4.1",
|
|
35
|
-
"commander": "^13.1.0",
|
|
36
|
-
"ejs": "^3.1.10",
|
|
37
|
-
"fs-extra": "^11.3.0",
|
|
38
|
-
"inquirer": "^12.4.1"
|
|
39
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "nodejs-quickstart-structure",
|
|
3
|
+
"version": "1.3.5",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "A CLI to scaffold Node.js microservices with MVC or Clean Architecture",
|
|
6
|
+
"main": "bin/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"nodejs-quickstart": "./bin/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
12
|
+
"test:e2e": "npm run test:e2e:windows",
|
|
13
|
+
"test:e2e:windows": "node scripts/validate-windows.js",
|
|
14
|
+
"test:e2e:linux": "node scripts/validate-linux.js"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"nodejs",
|
|
18
|
+
"cli",
|
|
19
|
+
"scaffold",
|
|
20
|
+
"mvc",
|
|
21
|
+
"clean-architecture"
|
|
22
|
+
],
|
|
23
|
+
"author": "Pau Dang <phucdangb1400718@gmail.com>",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/paudang/nodejs-quickstart-structure.git"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/paudang/nodejs-quickstart-structure/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/paudang/nodejs-quickstart-structure#readme",
|
|
32
|
+
"license": "ISC",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"chalk": "^5.4.1",
|
|
35
|
+
"commander": "^13.1.0",
|
|
36
|
+
"ejs": "^3.1.10",
|
|
37
|
+
"fs-extra": "^11.3.0",
|
|
38
|
+
"inquirer": "^12.4.1"
|
|
39
|
+
}
|
|
40
40
|
}
|
|
@@ -26,10 +26,10 @@ const syncDatabase = async () => {
|
|
|
26
26
|
});
|
|
27
27
|
<% } -%>
|
|
28
28
|
break;
|
|
29
|
-
} catch (
|
|
30
|
-
logger.error('
|
|
29
|
+
} catch (error) {
|
|
30
|
+
logger.error('Error syncing database:', error);
|
|
31
31
|
retries -= 1;
|
|
32
|
-
logger.info(`Retries left: ${retries}
|
|
32
|
+
logger.info(`Retries left: ${retries}`);
|
|
33
33
|
await new Promise(res => setTimeout(res, 5000));
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -13,10 +13,10 @@ const logger = winston.createLogger({
|
|
|
13
13
|
],
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
16
|
+
logger.add(new winston.transports.Console({
|
|
17
|
+
format: process.env.NODE_ENV !== 'production'
|
|
18
|
+
? winston.format.simple()
|
|
19
|
+
: winston.format.json(),
|
|
20
|
+
}));
|
|
21
21
|
|
|
22
22
|
module.exports = logger;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const cors = require('cors');
|
|
3
3
|
require('dotenv').config();
|
|
4
|
+
const logger = require('../log/logger');
|
|
4
5
|
<% if (communication === 'REST APIs') { %>const apiRoutes = require('../../interfaces/routes/api');<% } -%>
|
|
5
6
|
<% if (communication === 'REST APIs') { -%>
|
|
6
7
|
const swaggerUi = require('swagger-ui-express');
|
|
@@ -26,7 +27,7 @@ const startServer = (port) => {
|
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
app.listen(port, () => {
|
|
29
|
-
|
|
30
|
+
logger.info(`Server running on port ${port}`);
|
|
30
31
|
});
|
|
31
32
|
};
|
|
32
33
|
|
|
@@ -2,6 +2,7 @@ const CreateUser = require('../../usecases/CreateUser');
|
|
|
2
2
|
const GetAllUsers = require('../../usecases/GetAllUsers');
|
|
3
3
|
const UserRepository = require('../../infrastructure/repositories/UserRepository');
|
|
4
4
|
const HTTP_STATUS = require('../../utils/httpCodes');
|
|
5
|
+
const logger = require('../../infrastructure/log/logger');
|
|
5
6
|
|
|
6
7
|
class UserController {
|
|
7
8
|
constructor() {
|
|
@@ -13,7 +14,10 @@ class UserController {
|
|
|
13
14
|
getUsers(req, res) {
|
|
14
15
|
this.getAllUsersUseCase.execute()
|
|
15
16
|
.then(users => res.json(users))
|
|
16
|
-
.catch(err =>
|
|
17
|
+
.catch(err => {
|
|
18
|
+
logger.error('Error getting users:', err);
|
|
19
|
+
res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({ error: err.message });
|
|
20
|
+
});
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
async createUser(req, res) {
|
|
@@ -22,6 +26,7 @@ class UserController {
|
|
|
22
26
|
const user = await this.createUserUseCase.execute(name, email);
|
|
23
27
|
res.status(HTTP_STATUS.CREATED).json(user);
|
|
24
28
|
} catch (error) {
|
|
29
|
+
logger.error('Error creating user:', error);
|
|
25
30
|
res.status(HTTP_STATUS.BAD_REQUEST).json({ error: error.message });
|
|
26
31
|
}
|
|
27
32
|
}
|
|
@@ -61,10 +61,10 @@ const syncDatabase = async () => {
|
|
|
61
61
|
<%_ } -%>
|
|
62
62
|
});
|
|
63
63
|
break;
|
|
64
|
-
} catch (
|
|
65
|
-
logger.error('
|
|
64
|
+
} catch (error) {
|
|
65
|
+
logger.error('Error syncing database:', error);
|
|
66
66
|
retries -= 1;
|
|
67
|
-
logger.info(`Retries left: ${retries}
|
|
67
|
+
logger.info(`Retries left: ${retries}`);
|
|
68
68
|
await new Promise(res => setTimeout(res, 5000));
|
|
69
69
|
}
|
|
70
70
|
}
|
|
@@ -13,10 +13,10 @@ const logger = winston.createLogger({
|
|
|
13
13
|
],
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
16
|
+
logger.add(new winston.transports.Console({
|
|
17
|
+
format: process.env.NODE_ENV !== 'production'
|
|
18
|
+
? winston.format.simple()
|
|
19
|
+
: winston.format.json(),
|
|
20
|
+
}));
|
|
21
21
|
|
|
22
22
|
export default logger;
|
|
@@ -3,6 +3,7 @@ import { UserRepository } from '../../infrastructure/repositories/UserRepository
|
|
|
3
3
|
import CreateUser from '../../usecases/createUser';
|
|
4
4
|
import GetAllUsers from '../../usecases/getAllUsers';
|
|
5
5
|
import { HTTP_STATUS } from '../../utils/httpCodes';
|
|
6
|
+
import logger from '../../infrastructure/log/logger';
|
|
6
7
|
|
|
7
8
|
export class UserController {
|
|
8
9
|
private createUserUseCase: CreateUser;
|
|
@@ -20,7 +21,7 @@ export class UserController {
|
|
|
20
21
|
const user = await this.createUserUseCase.execute(name, email);
|
|
21
22
|
res.status(HTTP_STATUS.CREATED).json(user);
|
|
22
23
|
} catch (error) {
|
|
23
|
-
|
|
24
|
+
logger.error('UserController Error:', error);
|
|
24
25
|
if (error instanceof Error) {
|
|
25
26
|
res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({ error: error.message });
|
|
26
27
|
} else {
|
|
@@ -34,7 +35,7 @@ export class UserController {
|
|
|
34
35
|
const users = await this.getAllUsersUseCase.execute();
|
|
35
36
|
res.json(users);
|
|
36
37
|
} catch (error) {
|
|
37
|
-
|
|
38
|
+
logger.error('UserController Error:', error);
|
|
38
39
|
if (error instanceof Error) {
|
|
39
40
|
res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({ error: error.message });
|
|
40
41
|
} else {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { kafka } = require('../config/kafka');
|
|
2
|
+
const logger = require('<%= loggerPath %>');
|
|
2
3
|
|
|
3
4
|
const producer = kafka.producer();
|
|
4
5
|
const consumer = kafka.consumer({ groupId: 'test-group' });
|
|
@@ -10,7 +11,7 @@ const connectKafka = async () => {
|
|
|
10
11
|
|
|
11
12
|
await consumer.run({
|
|
12
13
|
eachMessage: async ({ topic, partition, message }) => {
|
|
13
|
-
|
|
14
|
+
logger.info({
|
|
14
15
|
value: message.value.toString(),
|
|
15
16
|
});
|
|
16
17
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { kafka } from '../config/kafka';
|
|
2
2
|
import { EachMessagePayload, Producer, Consumer } from 'kafkajs';
|
|
3
|
+
import logger from '<%= loggerPath %>';
|
|
3
4
|
|
|
4
5
|
export class KafkaService {
|
|
5
6
|
private producer: Producer;
|
|
@@ -17,7 +18,7 @@ export class KafkaService {
|
|
|
17
18
|
|
|
18
19
|
await this.consumer.run({
|
|
19
20
|
eachMessage: async ({ topic, partition, message }: EachMessagePayload) => {
|
|
20
|
-
|
|
21
|
+
logger.info({
|
|
21
22
|
value: message.value?.toString(),
|
|
22
23
|
});
|
|
23
24
|
},
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
const User = require('../models/User');
|
|
2
2
|
const HTTP_STATUS = require('../utils/httpCodes');
|
|
3
|
+
const logger = require('../utils/logger');
|
|
3
4
|
|
|
4
5
|
const getUsers = async (req, res) => {
|
|
5
6
|
try {
|
|
6
7
|
const users = await User.findAll();
|
|
7
8
|
res.json(users);
|
|
8
9
|
} catch (error) {
|
|
9
|
-
|
|
10
|
+
logger.error('Error fetching users:', error);
|
|
11
|
+
res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({ error: 'Internal Server Error' });
|
|
10
12
|
}
|
|
11
13
|
};
|
|
12
14
|
|
|
@@ -21,10 +21,10 @@ const logger = winston.createLogger({
|
|
|
21
21
|
// If we're not in production then log to the `console` with the format:
|
|
22
22
|
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
|
|
23
23
|
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
24
|
+
logger.add(new winston.transports.Console({
|
|
25
|
+
format: process.env.NODE_ENV !== 'production'
|
|
26
|
+
? winston.format.simple()
|
|
27
|
+
: winston.format.json(),
|
|
28
|
+
}));
|
|
29
29
|
|
|
30
30
|
module.exports = logger;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Request, Response } from 'express';
|
|
2
2
|
import User from '../models/User';
|
|
3
3
|
import { HTTP_STATUS } from '../utils/httpCodes';
|
|
4
|
+
import logger from '../utils/logger';
|
|
4
5
|
|
|
5
6
|
export class UserController {
|
|
6
7
|
async getUsers(req: Request, res: Response) {
|
|
@@ -8,6 +9,7 @@ export class UserController {
|
|
|
8
9
|
const users = await User.findAll();
|
|
9
10
|
res.json(users);
|
|
10
11
|
} catch (error) {
|
|
12
|
+
logger.error('Error fetching users:', error);
|
|
11
13
|
if (error instanceof Error) {
|
|
12
14
|
res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({ error: error.message });
|
|
13
15
|
} else {
|
|
@@ -22,6 +24,7 @@ export class UserController {
|
|
|
22
24
|
const user = await User.create({ name, email });
|
|
23
25
|
res.status(HTTP_STATUS.CREATED).json(user);
|
|
24
26
|
} catch (error) {
|
|
27
|
+
logger.error('Error creating user:', error);
|
|
25
28
|
if (error instanceof Error) {
|
|
26
29
|
res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({ error: error.message });
|
|
27
30
|
} else {
|
|
@@ -80,10 +80,10 @@ const syncDatabase = async () => {
|
|
|
80
80
|
<%_ } -%>
|
|
81
81
|
});
|
|
82
82
|
break;
|
|
83
|
-
} catch (
|
|
84
|
-
logger.error('
|
|
83
|
+
} catch (error) {
|
|
84
|
+
logger.error('Error syncing database:', error);
|
|
85
85
|
retries -= 1;
|
|
86
|
-
logger.info(`Retries left: ${retries}
|
|
86
|
+
logger.info(`Retries left: ${retries}`);
|
|
87
87
|
await new Promise(res => setTimeout(res, 5000));
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -13,10 +13,10 @@ const logger = winston.createLogger({
|
|
|
13
13
|
],
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
16
|
+
logger.add(new winston.transports.Console({
|
|
17
|
+
format: process.env.NODE_ENV !== 'production'
|
|
18
|
+
? winston.format.simple()
|
|
19
|
+
: winston.format.json(),
|
|
20
|
+
}));
|
|
21
21
|
|
|
22
22
|
export default logger;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
exports.getUsers = (req, res) => {
|
|
2
|
-
res.json([
|
|
3
|
-
{ id: 1, name: 'John Doe' },
|
|
4
|
-
{ id: 2, name: 'Jane Doe' }
|
|
5
|
-
]);
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
exports.createUser = (req, res) => {
|
|
9
|
-
const { name, email } = req.body;
|
|
10
|
-
res.status(201).json({
|
|
11
|
-
message: 'User created successfully',
|
|
12
|
-
user: { name, email }
|
|
13
|
-
});
|
|
14
|
-
};
|