nodejs-quickstart-structure 1.19.0 → 1.19.1
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/CHANGELOG.md +309 -301
- package/LICENSE +15 -15
- package/lib/generator.js +139 -139
- package/lib/modules/app-setup.js +401 -401
- package/lib/modules/config-files.js +151 -151
- package/lib/modules/database-setup.js +116 -116
- package/lib/modules/project-setup.js +32 -32
- package/lib/prompts.js +100 -100
- package/package.json +78 -78
- package/templates/clean-architecture/js/src/domain/models/User.js +9 -9
- package/templates/clean-architecture/js/src/errors/ApiError.js +14 -14
- package/templates/clean-architecture/js/src/index.js.ejs +55 -55
- package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +47 -47
- package/templates/clean-architecture/js/src/infrastructure/log/logger.js +36 -36
- package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -63
- package/templates/clean-architecture/js/src/infrastructure/webserver/middleware/errorMiddleware.js +30 -30
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +89 -89
- package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.js.ejs +6 -6
- package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -13
- package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -31
- package/templates/clean-architecture/js/src/interfaces/graphql/index.js.ejs +5 -5
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/index.js.ejs +6 -6
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/index.js.ejs +6 -6
- package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -38
- package/templates/clean-architecture/js/src/usecases/CreateUser.js +14 -14
- package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -51
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +12 -12
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -61
- package/templates/clean-architecture/js/src/utils/httpCodes.js +9 -9
- package/templates/clean-architecture/ts/src/config/env.ts.ejs +46 -46
- package/templates/clean-architecture/ts/src/config/swagger.ts.ejs +6 -6
- package/templates/clean-architecture/ts/src/domain/user.ts +7 -7
- package/templates/clean-architecture/ts/src/errors/ApiError.ts +15 -15
- package/templates/clean-architecture/ts/src/index.ts.ejs +139 -139
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +63 -63
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +36 -36
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -32
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +17 -17
- package/templates/clean-architecture/ts/src/interfaces/graphql/index.ts.ejs +3 -3
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/index.ts.ejs +4 -4
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/index.ts.ejs +4 -4
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -40
- package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -51
- package/templates/clean-architecture/ts/src/usecases/createUser.ts +13 -13
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -63
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +10 -10
- package/templates/clean-architecture/ts/src/utils/errorMiddleware.ts.ejs +27 -27
- package/templates/clean-architecture/ts/src/utils/httpCodes.ts +7 -7
- package/templates/common/.cursorrules.ejs +60 -60
- package/templates/common/.dockerignore +12 -12
- package/templates/common/.env.example.ejs +41 -41
- package/templates/common/.gitlab-ci.yml.ejs +86 -86
- package/templates/common/.lintstagedrc +6 -6
- package/templates/common/.prettierrc +7 -7
- package/templates/common/Dockerfile +73 -73
- package/templates/common/Jenkinsfile.ejs +87 -87
- package/templates/common/SECURITY.md +20 -20
- package/templates/common/_github/workflows/ci.yml.ejs +46 -46
- package/templates/common/_github/workflows/security.yml.ejs +36 -36
- package/templates/common/_gitignore +5 -5
- package/templates/common/_husky/pre-commit +4 -4
- package/templates/common/caching/clean/js/CreateUser.js.ejs +29 -29
- package/templates/common/caching/clean/js/GetAllUsers.js.ejs +37 -37
- package/templates/common/caching/clean/ts/createUser.ts.ejs +27 -27
- package/templates/common/caching/clean/ts/getAllUsers.ts.ejs +34 -34
- package/templates/common/caching/js/memoryCache.js.ejs +60 -60
- package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -101
- package/templates/common/caching/js/redisClient.js.ejs +75 -75
- package/templates/common/caching/js/redisClient.spec.js.ejs +147 -147
- package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -102
- package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -157
- package/templates/common/database/js/database.js.ejs +19 -19
- package/templates/common/database/js/database.spec.js.ejs +56 -56
- package/templates/common/database/js/mongoose.js.ejs +33 -33
- package/templates/common/database/js/mongoose.spec.js.ejs +43 -43
- package/templates/common/database/ts/database.spec.ts.ejs +56 -56
- package/templates/common/database/ts/database.ts.ejs +21 -21
- package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -42
- package/templates/common/database/ts/mongoose.ts.ejs +28 -28
- package/templates/common/docker-compose.yml.ejs +159 -159
- package/templates/common/ecosystem.config.js.ejs +40 -40
- package/templates/common/eslint.config.mjs.ejs +77 -77
- package/templates/common/health/js/healthRoute.spec.js.ejs +70 -70
- package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -76
- package/templates/common/jest.config.js.ejs +32 -32
- package/templates/common/kafka/js/config/kafka.js +9 -9
- package/templates/common/kafka/js/config/kafka.spec.js.ejs +27 -27
- package/templates/common/kafka/js/messaging/baseConsumer.spec.js.ejs +58 -58
- package/templates/common/kafka/js/messaging/userEventSchema.spec.js.ejs +27 -27
- package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +106 -106
- package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +27 -27
- package/templates/common/kafka/ts/config/kafka.ts +7 -7
- package/templates/common/kafka/ts/messaging/baseConsumer.spec.ts.ejs +50 -50
- package/templates/common/kafka/ts/messaging/baseConsumer.ts.ejs +27 -27
- package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +81 -81
- package/templates/common/migrate-mongo-config.js.ejs +31 -31
- package/templates/common/migrations/init.js.ejs +23 -23
- package/templates/common/package.json.ejs +119 -118
- package/templates/common/prompts/add-feature.md.ejs +26 -26
- package/templates/common/prompts/project-context.md.ejs +43 -43
- package/templates/common/prompts/troubleshoot.md.ejs +28 -28
- package/templates/common/public/css/style.css +147 -147
- package/templates/common/scripts/run-e2e.js.ejs +63 -63
- package/templates/common/sonar-project.properties.ejs +27 -27
- package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -79
- package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -94
- package/templates/common/tsconfig.json +22 -22
- package/templates/common/views/ejs/index.ejs +55 -55
- package/templates/common/views/pug/index.pug +40 -40
- package/templates/mvc/js/src/config/env.js.ejs +46 -46
- package/templates/mvc/js/src/config/swagger.js.ejs +6 -6
- package/templates/mvc/js/src/errors/ApiError.js +14 -14
- package/templates/mvc/js/src/graphql/context.js.ejs +7 -7
- package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -29
- package/templates/mvc/js/src/graphql/index.js.ejs +5 -5
- package/templates/mvc/js/src/graphql/resolvers/index.js.ejs +6 -6
- package/templates/mvc/js/src/graphql/typeDefs/index.js.ejs +6 -6
- package/templates/mvc/js/src/index.js.ejs +136 -136
- package/templates/mvc/js/src/utils/errorMiddleware.js +29 -29
- package/templates/mvc/js/src/utils/httpCodes.js +9 -9
- package/templates/mvc/js/src/utils/logger.js +40 -40
- package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -63
- package/templates/mvc/ts/src/config/env.ts.ejs +45 -45
- package/templates/mvc/ts/src/config/swagger.ts.ejs +6 -6
- package/templates/mvc/ts/src/errors/ApiError.ts +15 -15
- package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -30
- package/templates/mvc/ts/src/graphql/context.ts.ejs +12 -12
- package/templates/mvc/ts/src/graphql/index.ts.ejs +3 -3
- package/templates/mvc/ts/src/graphql/resolvers/index.ts.ejs +4 -4
- package/templates/mvc/ts/src/graphql/typeDefs/index.ts.ejs +4 -4
- package/templates/mvc/ts/src/utils/errorMiddleware.ts.ejs +27 -27
- package/templates/mvc/ts/src/utils/httpCodes.ts +7 -7
- package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +63 -63
- package/templates/mvc/ts/src/utils/logger.ts +36 -36
package/lib/prompts.js
CHANGED
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
import inquirer from 'inquirer';
|
|
2
|
-
|
|
3
|
-
const validateName = (name) => {
|
|
4
|
-
return /^[a-zA-Z0-9-_]+$/.test(name) ? true : 'Project name may only include letters, numbers, underscores and dashes.';
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
export const getProjectDetails = async (options = {}) => {
|
|
8
|
-
const questions = [
|
|
9
|
-
{
|
|
10
|
-
type: 'input',
|
|
11
|
-
name: 'projectName',
|
|
12
|
-
message: 'Project name:',
|
|
13
|
-
default: 'nodejs-service',
|
|
14
|
-
validate: validateName,
|
|
15
|
-
when: !options.projectName
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
type: 'select',
|
|
19
|
-
name: 'language',
|
|
20
|
-
message: 'Select Language:',
|
|
21
|
-
choices: ['JavaScript', 'TypeScript'],
|
|
22
|
-
default: 'TypeScript',
|
|
23
|
-
when: !options.language
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
type: 'select',
|
|
27
|
-
name: 'architecture',
|
|
28
|
-
message: 'Select Architecture:',
|
|
29
|
-
choices: ['MVC', 'Clean Architecture'],
|
|
30
|
-
default: 'MVC',
|
|
31
|
-
when: !options.architecture
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
type: 'select',
|
|
35
|
-
name: 'viewEngine',
|
|
36
|
-
message: 'Select View Engine:',
|
|
37
|
-
choices: ['None', 'EJS', 'Pug'],
|
|
38
|
-
when: (answers) => (options.architecture || answers.architecture) === 'MVC' && !options.viewEngine,
|
|
39
|
-
default: 'None'
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
type: 'select',
|
|
43
|
-
name: 'database',
|
|
44
|
-
message: 'Select Database:',
|
|
45
|
-
choices: ['None', 'MySQL', 'PostgreSQL', 'MongoDB'],
|
|
46
|
-
default: 'None',
|
|
47
|
-
when: !options.database
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
type: 'input',
|
|
51
|
-
name: 'dbName',
|
|
52
|
-
message: 'Database Name:',
|
|
53
|
-
default: 'demo',
|
|
54
|
-
validate: validateName,
|
|
55
|
-
when: (answers) => !options.dbName && (options.database || answers.database) !== 'None'
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
type: 'select',
|
|
59
|
-
name: 'communication',
|
|
60
|
-
message: 'Microservices Communication:',
|
|
61
|
-
choices: ['REST APIs', 'GraphQL', 'Kafka'],
|
|
62
|
-
default: 'REST APIs',
|
|
63
|
-
when: !options.communication
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
type: 'select',
|
|
67
|
-
name: 'caching',
|
|
68
|
-
message: 'Caching Layer:',
|
|
69
|
-
choices: ['None', 'Redis', 'Memory Cache'],
|
|
70
|
-
default: 'None',
|
|
71
|
-
when: (answers) => !options.caching && (options.database || answers.database) !== 'None'
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
type: 'select',
|
|
75
|
-
name: 'ciProvider',
|
|
76
|
-
message: 'Select CI/CD Provider:',
|
|
77
|
-
choices: ['None', 'GitHub Actions', 'Jenkins', 'GitLab CI'],
|
|
78
|
-
default: 'None',
|
|
79
|
-
when: !options.ciProvider
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
type: 'select',
|
|
83
|
-
name: 'includeSecurity',
|
|
84
|
-
message: 'Include Enterprise Security Hardening (Big Tech Standard: Snyk, SonarQube)?',
|
|
85
|
-
choices: ['No', 'Yes'],
|
|
86
|
-
default: "No",
|
|
87
|
-
when: (answers) => !options.includeSecurity && (options.ciProvider || answers.ciProvider) !== 'None'
|
|
88
|
-
}
|
|
89
|
-
];
|
|
90
|
-
|
|
91
|
-
const answers = await inquirer.prompt(questions);
|
|
92
|
-
const result = { ...options, ...answers };
|
|
93
|
-
|
|
94
|
-
// Normalize includeSecurity to boolean if it's a string from the select prompt
|
|
95
|
-
if (typeof result.includeSecurity === 'string') {
|
|
96
|
-
result.includeSecurity = result.includeSecurity === 'Yes';
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return result;
|
|
100
|
-
};
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
|
|
3
|
+
const validateName = (name) => {
|
|
4
|
+
return /^[a-zA-Z0-9-_]+$/.test(name) ? true : 'Project name may only include letters, numbers, underscores and dashes.';
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const getProjectDetails = async (options = {}) => {
|
|
8
|
+
const questions = [
|
|
9
|
+
{
|
|
10
|
+
type: 'input',
|
|
11
|
+
name: 'projectName',
|
|
12
|
+
message: 'Project name:',
|
|
13
|
+
default: 'nodejs-service',
|
|
14
|
+
validate: validateName,
|
|
15
|
+
when: !options.projectName
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
type: 'select',
|
|
19
|
+
name: 'language',
|
|
20
|
+
message: 'Select Language:',
|
|
21
|
+
choices: ['JavaScript', 'TypeScript'],
|
|
22
|
+
default: 'TypeScript',
|
|
23
|
+
when: !options.language
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: 'select',
|
|
27
|
+
name: 'architecture',
|
|
28
|
+
message: 'Select Architecture:',
|
|
29
|
+
choices: ['MVC', 'Clean Architecture'],
|
|
30
|
+
default: 'MVC',
|
|
31
|
+
when: !options.architecture
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
type: 'select',
|
|
35
|
+
name: 'viewEngine',
|
|
36
|
+
message: 'Select View Engine:',
|
|
37
|
+
choices: ['None', 'EJS', 'Pug'],
|
|
38
|
+
when: (answers) => (options.architecture || answers.architecture) === 'MVC' && !options.viewEngine,
|
|
39
|
+
default: 'None'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
type: 'select',
|
|
43
|
+
name: 'database',
|
|
44
|
+
message: 'Select Database:',
|
|
45
|
+
choices: ['None', 'MySQL', 'PostgreSQL', 'MongoDB'],
|
|
46
|
+
default: 'None',
|
|
47
|
+
when: !options.database
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
type: 'input',
|
|
51
|
+
name: 'dbName',
|
|
52
|
+
message: 'Database Name:',
|
|
53
|
+
default: 'demo',
|
|
54
|
+
validate: validateName,
|
|
55
|
+
when: (answers) => !options.dbName && (options.database || answers.database) !== 'None'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: 'select',
|
|
59
|
+
name: 'communication',
|
|
60
|
+
message: 'Microservices Communication:',
|
|
61
|
+
choices: ['REST APIs', 'GraphQL', 'Kafka'],
|
|
62
|
+
default: 'REST APIs',
|
|
63
|
+
when: !options.communication
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
type: 'select',
|
|
67
|
+
name: 'caching',
|
|
68
|
+
message: 'Caching Layer:',
|
|
69
|
+
choices: ['None', 'Redis', 'Memory Cache'],
|
|
70
|
+
default: 'None',
|
|
71
|
+
when: (answers) => !options.caching && (options.database || answers.database) !== 'None'
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: 'select',
|
|
75
|
+
name: 'ciProvider',
|
|
76
|
+
message: 'Select CI/CD Provider:',
|
|
77
|
+
choices: ['None', 'GitHub Actions', 'Jenkins', 'GitLab CI'],
|
|
78
|
+
default: 'None',
|
|
79
|
+
when: !options.ciProvider
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
type: 'select',
|
|
83
|
+
name: 'includeSecurity',
|
|
84
|
+
message: 'Include Enterprise Security Hardening (Big Tech Standard: Snyk, SonarQube)?',
|
|
85
|
+
choices: ['No', 'Yes'],
|
|
86
|
+
default: "No",
|
|
87
|
+
when: (answers) => !options.includeSecurity && (options.ciProvider || answers.ciProvider) !== 'None'
|
|
88
|
+
}
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
const answers = await inquirer.prompt(questions);
|
|
92
|
+
const result = { ...options, ...answers };
|
|
93
|
+
|
|
94
|
+
// Normalize includeSecurity to boolean if it's a string from the select prompt
|
|
95
|
+
if (typeof result.includeSecurity === 'string') {
|
|
96
|
+
result.includeSecurity = result.includeSecurity === 'Yes';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return result;
|
|
100
|
+
};
|
package/package.json
CHANGED
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "nodejs-quickstart-structure",
|
|
3
|
-
"version": "1.19.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "The ultimate nodejs quickstart structure 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
|
-
"test:verify:mongo": "node scripts/verify-migration.js",
|
|
16
|
-
"docs:dev": "vitepress dev docs",
|
|
17
|
-
"docs:build": "vitepress build docs",
|
|
18
|
-
"docs:preview": "vitepress preview docs",
|
|
19
|
-
"security:check": "npm audit && npm run snyk:test",
|
|
20
|
-
"snyk:test": "snyk test"
|
|
21
|
-
},
|
|
22
|
-
"keywords": [
|
|
23
|
-
"nodejs",
|
|
24
|
-
"node",
|
|
25
|
-
"quickstart",
|
|
26
|
-
"structure",
|
|
27
|
-
"cli",
|
|
28
|
-
"scaffold",
|
|
29
|
-
"mvc",
|
|
30
|
-
"clean-architecture",
|
|
31
|
-
"microservices",
|
|
32
|
-
"backend",
|
|
33
|
-
"generator",
|
|
34
|
-
"boilerplate"
|
|
35
|
-
],
|
|
36
|
-
"author": "Pau Dang <[phucdangb1400718@gmail.com]>",
|
|
37
|
-
"contributors": [
|
|
38
|
-
{
|
|
39
|
-
"name": "Pau Dang",
|
|
40
|
-
"url": "https://github.com/paudang"
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"name": "Gemini AI",
|
|
44
|
-
"url": "https://deepmind.google/technologies/gemini/",
|
|
45
|
-
"role": "AI Architectural Assistant"
|
|
46
|
-
}
|
|
47
|
-
],
|
|
48
|
-
"repository": {
|
|
49
|
-
"type": "git",
|
|
50
|
-
"url": "git+https://github.com/paudang/nodejs-quickstart-structure.git"
|
|
51
|
-
},
|
|
52
|
-
"bugs": {
|
|
53
|
-
"url": "https://github.com/paudang/nodejs-quickstart-structure/issues"
|
|
54
|
-
},
|
|
55
|
-
"homepage": "https://github.com/paudang/nodejs-quickstart-structure#readme",
|
|
56
|
-
"license": "ISC",
|
|
57
|
-
"dependencies": {
|
|
58
|
-
"chalk": "^5.4.1",
|
|
59
|
-
"commander": "^14.0.3",
|
|
60
|
-
"ejs": "^5.0.1",
|
|
61
|
-
"fs-extra": "^11.3.0",
|
|
62
|
-
"inquirer": "^13.3.2"
|
|
63
|
-
},
|
|
64
|
-
"overrides": {
|
|
65
|
-
"esbuild": "^0.25.0"
|
|
66
|
-
},
|
|
67
|
-
"devDependencies": {
|
|
68
|
-
"snyk": "^1.1303.2",
|
|
69
|
-
"vitepress": "^1.0.0-rc.45"
|
|
70
|
-
},
|
|
71
|
-
"files": [
|
|
72
|
-
"bin",
|
|
73
|
-
"lib",
|
|
74
|
-
"templates",
|
|
75
|
-
"README.md",
|
|
76
|
-
"CHANGELOG.md"
|
|
77
|
-
]
|
|
78
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "nodejs-quickstart-structure",
|
|
3
|
+
"version": "1.19.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "The ultimate nodejs quickstart structure 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
|
+
"test:verify:mongo": "node scripts/verify-migration.js",
|
|
16
|
+
"docs:dev": "vitepress dev docs",
|
|
17
|
+
"docs:build": "vitepress build docs",
|
|
18
|
+
"docs:preview": "vitepress preview docs",
|
|
19
|
+
"security:check": "npm audit && npm run snyk:test",
|
|
20
|
+
"snyk:test": "snyk test"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"nodejs",
|
|
24
|
+
"node",
|
|
25
|
+
"quickstart",
|
|
26
|
+
"structure",
|
|
27
|
+
"cli",
|
|
28
|
+
"scaffold",
|
|
29
|
+
"mvc",
|
|
30
|
+
"clean-architecture",
|
|
31
|
+
"microservices",
|
|
32
|
+
"backend",
|
|
33
|
+
"generator",
|
|
34
|
+
"boilerplate"
|
|
35
|
+
],
|
|
36
|
+
"author": "Pau Dang <[phucdangb1400718@gmail.com]>",
|
|
37
|
+
"contributors": [
|
|
38
|
+
{
|
|
39
|
+
"name": "Pau Dang",
|
|
40
|
+
"url": "https://github.com/paudang"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "Gemini AI",
|
|
44
|
+
"url": "https://deepmind.google/technologies/gemini/",
|
|
45
|
+
"role": "AI Architectural Assistant"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/paudang/nodejs-quickstart-structure.git"
|
|
51
|
+
},
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/paudang/nodejs-quickstart-structure/issues"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/paudang/nodejs-quickstart-structure#readme",
|
|
56
|
+
"license": "ISC",
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"chalk": "^5.4.1",
|
|
59
|
+
"commander": "^14.0.3",
|
|
60
|
+
"ejs": "^5.0.1",
|
|
61
|
+
"fs-extra": "^11.3.0",
|
|
62
|
+
"inquirer": "^13.3.2"
|
|
63
|
+
},
|
|
64
|
+
"overrides": {
|
|
65
|
+
"esbuild": "^0.25.0"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"snyk": "^1.1303.2",
|
|
69
|
+
"vitepress": "^1.0.0-rc.45"
|
|
70
|
+
},
|
|
71
|
+
"files": [
|
|
72
|
+
"bin",
|
|
73
|
+
"lib",
|
|
74
|
+
"templates",
|
|
75
|
+
"README.md",
|
|
76
|
+
"CHANGELOG.md"
|
|
77
|
+
]
|
|
78
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
class User {
|
|
2
|
-
constructor(id, name, email) {
|
|
3
|
-
this.id = id;
|
|
4
|
-
this.name = name;
|
|
5
|
-
this.email = email;
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
module.exports = User;
|
|
1
|
+
class User {
|
|
2
|
+
constructor(id, name, email) {
|
|
3
|
+
this.id = id;
|
|
4
|
+
this.name = name;
|
|
5
|
+
this.email = email;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
module.exports = User;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
class ApiError extends Error {
|
|
2
|
-
constructor(statusCode, message, isOperational = true, stack = '') {
|
|
3
|
-
super(message);
|
|
4
|
-
this.statusCode = statusCode;
|
|
5
|
-
this.isOperational = isOperational;
|
|
6
|
-
if (stack) {
|
|
7
|
-
this.stack = stack;
|
|
8
|
-
} else {
|
|
9
|
-
Error.captureStackTrace(this, this.constructor);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
module.exports = { ApiError };
|
|
1
|
+
class ApiError extends Error {
|
|
2
|
+
constructor(statusCode, message, isOperational = true, stack = '') {
|
|
3
|
+
super(message);
|
|
4
|
+
this.statusCode = statusCode;
|
|
5
|
+
this.isOperational = isOperational;
|
|
6
|
+
if (stack) {
|
|
7
|
+
this.stack = stack;
|
|
8
|
+
} else {
|
|
9
|
+
Error.captureStackTrace(this, this.constructor);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = { ApiError };
|
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
const startServer = require('./infrastructure/webserver/server');
|
|
2
|
-
const logger = require('./infrastructure/log/logger');
|
|
3
|
-
<% if (communication === 'Kafka') { -%>
|
|
4
|
-
const { connectKafka } = require('./infrastructure/messaging/kafkaClient');
|
|
5
|
-
<% } -%>
|
|
6
|
-
<%_ if (database !== 'None') { -%>
|
|
7
|
-
// Database Sync
|
|
8
|
-
<%_ if (database !== 'None') { -%>
|
|
9
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
10
|
-
const connectDB = require('./infrastructure/database/database');
|
|
11
|
-
<%_ } else { -%>
|
|
12
|
-
const sequelize = require('./infrastructure/database/database');
|
|
13
|
-
<%_ } -%>
|
|
14
|
-
<%_ } -%>
|
|
15
|
-
|
|
16
|
-
const syncDatabase = async () => {
|
|
17
|
-
let retries = 30;
|
|
18
|
-
while (retries) {
|
|
19
|
-
try {
|
|
20
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
21
|
-
await connectDB();
|
|
22
|
-
<%_ } else { -%>
|
|
23
|
-
await sequelize.sync();
|
|
24
|
-
<%_ } -%>
|
|
25
|
-
logger.info('Database synced');
|
|
26
|
-
// Start the web server after DB sync
|
|
27
|
-
startServer();
|
|
28
|
-
<%_ if (communication === 'Kafka') { -%>
|
|
29
|
-
// Connect Kafka
|
|
30
|
-
connectKafka().then(async () => {
|
|
31
|
-
logger.info('Kafka connected');
|
|
32
|
-
}).catch(err => {
|
|
33
|
-
logger.error('Failed to connect to Kafka:', err);
|
|
34
|
-
});
|
|
35
|
-
<%_ } -%>
|
|
36
|
-
break;
|
|
37
|
-
} catch (error) {
|
|
38
|
-
logger.error('Error syncing database:', error);
|
|
39
|
-
retries -= 1;
|
|
40
|
-
logger.info(`Retries left: ${retries}`);
|
|
41
|
-
await new Promise(res => setTimeout(res, 5000));
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
syncDatabase();
|
|
46
|
-
<%_ } else { -%>
|
|
47
|
-
startServer();
|
|
48
|
-
<%_ if (communication === 'Kafka') { -%>
|
|
49
|
-
// Connect Kafka
|
|
50
|
-
connectKafka().then(async () => {
|
|
51
|
-
logger.info('Kafka connected');
|
|
52
|
-
}).catch(err => {
|
|
53
|
-
logger.error('Failed to connect to Kafka:', err);
|
|
54
|
-
});
|
|
55
|
-
<%_ } -%>
|
|
1
|
+
const startServer = require('./infrastructure/webserver/server');
|
|
2
|
+
const logger = require('./infrastructure/log/logger');
|
|
3
|
+
<% if (communication === 'Kafka') { -%>
|
|
4
|
+
const { connectKafka } = require('./infrastructure/messaging/kafkaClient');
|
|
5
|
+
<% } -%>
|
|
6
|
+
<%_ if (database !== 'None') { -%>
|
|
7
|
+
// Database Sync
|
|
8
|
+
<%_ if (database !== 'None') { -%>
|
|
9
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
10
|
+
const connectDB = require('./infrastructure/database/database');
|
|
11
|
+
<%_ } else { -%>
|
|
12
|
+
const sequelize = require('./infrastructure/database/database');
|
|
13
|
+
<%_ } -%>
|
|
14
|
+
<%_ } -%>
|
|
15
|
+
|
|
16
|
+
const syncDatabase = async () => {
|
|
17
|
+
let retries = 30;
|
|
18
|
+
while (retries) {
|
|
19
|
+
try {
|
|
20
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
21
|
+
await connectDB();
|
|
22
|
+
<%_ } else { -%>
|
|
23
|
+
await sequelize.sync();
|
|
24
|
+
<%_ } -%>
|
|
25
|
+
logger.info('Database synced');
|
|
26
|
+
// Start the web server after DB sync
|
|
27
|
+
startServer();
|
|
28
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
29
|
+
// Connect Kafka
|
|
30
|
+
connectKafka().then(async () => {
|
|
31
|
+
logger.info('Kafka connected');
|
|
32
|
+
}).catch(err => {
|
|
33
|
+
logger.error('Failed to connect to Kafka:', err);
|
|
34
|
+
});
|
|
35
|
+
<%_ } -%>
|
|
36
|
+
break;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
logger.error('Error syncing database:', error);
|
|
39
|
+
retries -= 1;
|
|
40
|
+
logger.info(`Retries left: ${retries}`);
|
|
41
|
+
await new Promise(res => setTimeout(res, 5000));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
syncDatabase();
|
|
46
|
+
<%_ } else { -%>
|
|
47
|
+
startServer();
|
|
48
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
49
|
+
// Connect Kafka
|
|
50
|
+
connectKafka().then(async () => {
|
|
51
|
+
logger.info('Kafka connected');
|
|
52
|
+
}).catch(err => {
|
|
53
|
+
logger.error('Failed to connect to Kafka:', err);
|
|
54
|
+
});
|
|
55
|
+
<%_ } -%>
|
|
56
56
|
<%_ } -%>
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
const { z } = require('zod');
|
|
2
|
-
const logger = require('../log/logger');
|
|
3
|
-
|
|
4
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
5
|
-
require('dotenv').config();
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const envSchema = z.object({
|
|
9
|
-
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
|
10
|
-
PORT: z.string().transform(Number).default('3000'),
|
|
11
|
-
<%_ if (database !== 'None') { -%>
|
|
12
|
-
DB_HOST: z.string(),
|
|
13
|
-
<%_ if (database === 'MySQL') { -%>
|
|
14
|
-
DB_USER: z.string(),
|
|
15
|
-
DB_PASSWORD: z.string(),
|
|
16
|
-
DB_NAME: z.string(),
|
|
17
|
-
DB_PORT: z.string().transform(Number),
|
|
18
|
-
<%_ } else if (database === 'PostgreSQL') { -%>
|
|
19
|
-
DB_USER: z.string(),
|
|
20
|
-
DB_PASSWORD: z.string(),
|
|
21
|
-
DB_NAME: z.string(),
|
|
22
|
-
DB_PORT: z.string().transform(Number),
|
|
23
|
-
<%_ } else if (database === 'MongoDB') { -%>
|
|
24
|
-
DB_NAME: z.string(),
|
|
25
|
-
DB_PORT: z.string().transform(Number),
|
|
26
|
-
<%_ } -%>
|
|
27
|
-
<%_ } -%>
|
|
28
|
-
<%_ if (caching === 'Redis') { -%>
|
|
29
|
-
REDIS_HOST: z.string(),
|
|
30
|
-
REDIS_PORT: z.string().transform(Number),
|
|
31
|
-
REDIS_PASSWORD: z.string().optional(),
|
|
32
|
-
<%_ } -%>
|
|
33
|
-
<%_ if (communication === 'Kafka') { -%>
|
|
34
|
-
KAFKA_BROKER: z.string(),
|
|
35
|
-
<%_ } -%>
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const _env = envSchema.safeParse(process.env);
|
|
39
|
-
|
|
40
|
-
if (!_env.success) {
|
|
41
|
-
logger.error('❌ Invalid environment variables:', _env.error.format());
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const env = _env.data;
|
|
46
|
-
|
|
47
|
-
module.exports = { env };
|
|
1
|
+
const { z } = require('zod');
|
|
2
|
+
const logger = require('../log/logger');
|
|
3
|
+
|
|
4
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
5
|
+
require('dotenv').config();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const envSchema = z.object({
|
|
9
|
+
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
|
10
|
+
PORT: z.string().transform(Number).default('3000'),
|
|
11
|
+
<%_ if (database !== 'None') { -%>
|
|
12
|
+
DB_HOST: z.string(),
|
|
13
|
+
<%_ if (database === 'MySQL') { -%>
|
|
14
|
+
DB_USER: z.string(),
|
|
15
|
+
DB_PASSWORD: z.string(),
|
|
16
|
+
DB_NAME: z.string(),
|
|
17
|
+
DB_PORT: z.string().transform(Number),
|
|
18
|
+
<%_ } else if (database === 'PostgreSQL') { -%>
|
|
19
|
+
DB_USER: z.string(),
|
|
20
|
+
DB_PASSWORD: z.string(),
|
|
21
|
+
DB_NAME: z.string(),
|
|
22
|
+
DB_PORT: z.string().transform(Number),
|
|
23
|
+
<%_ } else if (database === 'MongoDB') { -%>
|
|
24
|
+
DB_NAME: z.string(),
|
|
25
|
+
DB_PORT: z.string().transform(Number),
|
|
26
|
+
<%_ } -%>
|
|
27
|
+
<%_ } -%>
|
|
28
|
+
<%_ if (caching === 'Redis') { -%>
|
|
29
|
+
REDIS_HOST: z.string(),
|
|
30
|
+
REDIS_PORT: z.string().transform(Number),
|
|
31
|
+
REDIS_PASSWORD: z.string().optional(),
|
|
32
|
+
<%_ } -%>
|
|
33
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
34
|
+
KAFKA_BROKER: z.string(),
|
|
35
|
+
<%_ } -%>
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const _env = envSchema.safeParse(process.env);
|
|
39
|
+
|
|
40
|
+
if (!_env.success) {
|
|
41
|
+
logger.error('❌ Invalid environment variables:', _env.error.format());
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const env = _env.data;
|
|
46
|
+
|
|
47
|
+
module.exports = { env };
|