nodejs-structure-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.
Files changed (243) hide show
  1. package/README.md +32 -0
  2. package/bin/index.js +143 -0
  3. package/lib/generator.js +145 -0
  4. package/lib/modules/app-setup.js +479 -0
  5. package/lib/modules/caching-setup.js +76 -0
  6. package/lib/modules/config-files.js +151 -0
  7. package/lib/modules/database-setup.js +116 -0
  8. package/lib/modules/kafka-setup.js +249 -0
  9. package/lib/modules/project-setup.js +32 -0
  10. package/lib/prompts.js +128 -0
  11. package/package.json +66 -0
  12. package/templates/clean-architecture/js/src/domain/models/User.js.ejs +11 -0
  13. package/templates/clean-architecture/js/src/errors/ApiError.js +14 -0
  14. package/templates/clean-architecture/js/src/errors/BadRequestError.js +11 -0
  15. package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +22 -0
  16. package/templates/clean-architecture/js/src/errors/NotFoundError.js +11 -0
  17. package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +22 -0
  18. package/templates/clean-architecture/js/src/index.js.ejs +56 -0
  19. package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +47 -0
  20. package/templates/clean-architecture/js/src/infrastructure/log/logger.js +36 -0
  21. package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -0
  22. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +88 -0
  23. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +142 -0
  24. package/templates/clean-architecture/js/src/infrastructure/webserver/middleware/errorMiddleware.js +30 -0
  25. package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +93 -0
  26. package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.js.ejs +6 -0
  27. package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +190 -0
  28. package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +234 -0
  29. package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -0
  30. package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -0
  31. package/templates/clean-architecture/js/src/interfaces/graphql/index.js.ejs +5 -0
  32. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/index.js.ejs +6 -0
  33. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +27 -0
  34. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +66 -0
  35. package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/index.js.ejs +6 -0
  36. package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +19 -0
  37. package/templates/clean-architecture/js/src/interfaces/routes/api.js.ejs +17 -0
  38. package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -0
  39. package/templates/clean-architecture/js/src/usecases/CreateUser.js.ejs +14 -0
  40. package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -0
  41. package/templates/clean-architecture/js/src/usecases/DeleteUser.js +11 -0
  42. package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +47 -0
  43. package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +12 -0
  44. package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -0
  45. package/templates/clean-architecture/js/src/usecases/UpdateUser.js.ejs +11 -0
  46. package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +48 -0
  47. package/templates/clean-architecture/js/src/utils/errorMessages.js +14 -0
  48. package/templates/clean-architecture/js/src/utils/httpCodes.js +9 -0
  49. package/templates/clean-architecture/ts/src/config/env.ts.ejs +46 -0
  50. package/templates/clean-architecture/ts/src/config/swagger.ts.ejs +6 -0
  51. package/templates/clean-architecture/ts/src/domain/user.ts.ejs +9 -0
  52. package/templates/clean-architecture/ts/src/errors/ApiError.ts +15 -0
  53. package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +22 -0
  54. package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +9 -0
  55. package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +22 -0
  56. package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +9 -0
  57. package/templates/clean-architecture/ts/src/index.ts.ejs +144 -0
  58. package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +63 -0
  59. package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +36 -0
  60. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +175 -0
  61. package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +125 -0
  62. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +331 -0
  63. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +208 -0
  64. package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -0
  65. package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +17 -0
  66. package/templates/clean-architecture/ts/src/interfaces/graphql/index.ts.ejs +3 -0
  67. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/index.ts.ejs +4 -0
  68. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -0
  69. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +29 -0
  70. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/index.ts.ejs +4 -0
  71. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +17 -0
  72. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -0
  73. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts.ejs +18 -0
  74. package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -0
  75. package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +11 -0
  76. package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +47 -0
  77. package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +9 -0
  78. package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -0
  79. package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +10 -0
  80. package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +48 -0
  81. package/templates/clean-architecture/ts/src/usecases/updateUser.ts.ejs +10 -0
  82. package/templates/clean-architecture/ts/src/utils/errorMessages.ts +12 -0
  83. package/templates/clean-architecture/ts/src/utils/errorMiddleware.ts.ejs +27 -0
  84. package/templates/clean-architecture/ts/src/utils/httpCodes.ts +7 -0
  85. package/templates/common/.cursorrules.ejs +60 -0
  86. package/templates/common/.dockerignore +12 -0
  87. package/templates/common/.env.example.ejs +60 -0
  88. package/templates/common/.gitattributes +46 -0
  89. package/templates/common/.gitlab-ci.yml.ejs +86 -0
  90. package/templates/common/.lintstagedrc +6 -0
  91. package/templates/common/.prettierrc +7 -0
  92. package/templates/common/.snyk.ejs +45 -0
  93. package/templates/common/Dockerfile +73 -0
  94. package/templates/common/Jenkinsfile.ejs +87 -0
  95. package/templates/common/README.md.ejs +148 -0
  96. package/templates/common/_github/workflows/ci.yml.ejs +46 -0
  97. package/templates/common/_github/workflows/security.yml.ejs +36 -0
  98. package/templates/common/_gitignore +5 -0
  99. package/templates/common/_husky/pre-commit +4 -0
  100. package/templates/common/caching/clean/js/CreateUser.js.ejs +29 -0
  101. package/templates/common/caching/clean/js/DeleteUser.js.ejs +27 -0
  102. package/templates/common/caching/clean/js/GetAllUsers.js.ejs +37 -0
  103. package/templates/common/caching/clean/js/UpdateUser.js.ejs +27 -0
  104. package/templates/common/caching/clean/ts/createUser.ts.ejs +27 -0
  105. package/templates/common/caching/clean/ts/deleteUser.ts.ejs +24 -0
  106. package/templates/common/caching/clean/ts/getAllUsers.ts.ejs +34 -0
  107. package/templates/common/caching/clean/ts/updateUser.ts.ejs +25 -0
  108. package/templates/common/caching/js/memoryCache.js.ejs +60 -0
  109. package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -0
  110. package/templates/common/caching/js/redisClient.js.ejs +75 -0
  111. package/templates/common/caching/js/redisClient.spec.js.ejs +147 -0
  112. package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -0
  113. package/templates/common/caching/ts/memoryCache.ts.ejs +73 -0
  114. package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -0
  115. package/templates/common/caching/ts/redisClient.ts.ejs +89 -0
  116. package/templates/common/database/js/database.js.ejs +19 -0
  117. package/templates/common/database/js/database.spec.js.ejs +56 -0
  118. package/templates/common/database/js/models/User.js.ejs +91 -0
  119. package/templates/common/database/js/models/User.js.mongoose.ejs +35 -0
  120. package/templates/common/database/js/models/User.spec.js.ejs +94 -0
  121. package/templates/common/database/js/mongoose.js.ejs +33 -0
  122. package/templates/common/database/js/mongoose.spec.js.ejs +43 -0
  123. package/templates/common/database/ts/database.spec.ts.ejs +56 -0
  124. package/templates/common/database/ts/database.ts.ejs +21 -0
  125. package/templates/common/database/ts/models/User.spec.ts.ejs +100 -0
  126. package/templates/common/database/ts/models/User.ts.ejs +102 -0
  127. package/templates/common/database/ts/models/User.ts.mongoose.ejs +34 -0
  128. package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -0
  129. package/templates/common/database/ts/mongoose.ts.ejs +28 -0
  130. package/templates/common/docker-compose.yml.ejs +159 -0
  131. package/templates/common/ecosystem.config.js.ejs +40 -0
  132. package/templates/common/eslint.config.mjs.ejs +77 -0
  133. package/templates/common/health/js/healthRoute.js.ejs +50 -0
  134. package/templates/common/health/js/healthRoute.spec.js.ejs +70 -0
  135. package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -0
  136. package/templates/common/health/ts/healthRoute.ts.ejs +49 -0
  137. package/templates/common/jest.config.js.ejs +32 -0
  138. package/templates/common/jest.e2e.config.js.ejs +8 -0
  139. package/templates/common/kafka/js/config/kafka.js +9 -0
  140. package/templates/common/kafka/js/config/kafka.spec.js.ejs +27 -0
  141. package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +30 -0
  142. package/templates/common/kafka/js/messaging/baseConsumer.spec.js.ejs +58 -0
  143. package/templates/common/kafka/js/messaging/userEventSchema.js.ejs +12 -0
  144. package/templates/common/kafka/js/messaging/userEventSchema.spec.js.ejs +27 -0
  145. package/templates/common/kafka/js/messaging/welcomeEmailConsumer.js.ejs +44 -0
  146. package/templates/common/kafka/js/messaging/welcomeEmailConsumer.spec.js.ejs +86 -0
  147. package/templates/common/kafka/js/services/kafkaService.js.ejs +93 -0
  148. package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +106 -0
  149. package/templates/common/kafka/js/utils/kafkaEvents.js.ejs +7 -0
  150. package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +27 -0
  151. package/templates/common/kafka/ts/config/kafka.ts +7 -0
  152. package/templates/common/kafka/ts/messaging/baseConsumer.spec.ts.ejs +50 -0
  153. package/templates/common/kafka/ts/messaging/baseConsumer.ts.ejs +27 -0
  154. package/templates/common/kafka/ts/messaging/userEventSchema.spec.ts.ejs +51 -0
  155. package/templates/common/kafka/ts/messaging/userEventSchema.ts.ejs +12 -0
  156. package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.spec.ts.ejs +86 -0
  157. package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.ts.ejs +38 -0
  158. package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +81 -0
  159. package/templates/common/kafka/ts/services/kafkaService.ts.ejs +95 -0
  160. package/templates/common/kafka/ts/utils/kafkaEvents.ts.ejs +5 -0
  161. package/templates/common/migrate-mongo-config.js.ejs +31 -0
  162. package/templates/common/migrations/init.js.ejs +23 -0
  163. package/templates/common/package.json.ejs +137 -0
  164. package/templates/common/prompts/add-feature.md.ejs +26 -0
  165. package/templates/common/prompts/project-context.md.ejs +43 -0
  166. package/templates/common/prompts/troubleshoot.md.ejs +28 -0
  167. package/templates/common/public/css/style.css +147 -0
  168. package/templates/common/scripts/run-e2e.js.ejs +63 -0
  169. package/templates/common/shutdown/js/gracefulShutdown.js.ejs +65 -0
  170. package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +149 -0
  171. package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +179 -0
  172. package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +59 -0
  173. package/templates/common/sonar-project.properties.ejs +27 -0
  174. package/templates/common/src/config/auth.js.ejs +19 -0
  175. package/templates/common/src/config/auth.ts.ejs +19 -0
  176. package/templates/common/src/controllers/authController.js.ejs +101 -0
  177. package/templates/common/src/controllers/authController.ts.ejs +101 -0
  178. package/templates/common/src/middleware/auth.js.ejs +20 -0
  179. package/templates/common/src/middleware/auth.ts.ejs +25 -0
  180. package/templates/common/src/middleware/upload.js.ejs +31 -0
  181. package/templates/common/src/middleware/upload.ts.ejs +32 -0
  182. package/templates/common/src/routes/authRoutes.js.ejs +20 -0
  183. package/templates/common/src/routes/authRoutes.ts.ejs +20 -0
  184. package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +120 -0
  185. package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +120 -0
  186. package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -0
  187. package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -0
  188. package/templates/common/swagger.yml.ejs +118 -0
  189. package/templates/common/tsconfig.json +23 -0
  190. package/templates/common/views/ejs/index.ejs +55 -0
  191. package/templates/common/views/pug/index.pug +40 -0
  192. package/templates/db/mysql/V1__Initial_Setup.sql.ejs +10 -0
  193. package/templates/db/postgres/V1__Initial_Setup.sql.ejs +10 -0
  194. package/templates/mvc/js/src/config/env.js.ejs +46 -0
  195. package/templates/mvc/js/src/config/swagger.js.ejs +6 -0
  196. package/templates/mvc/js/src/controllers/userController.js.ejs +288 -0
  197. package/templates/mvc/js/src/controllers/userController.spec.js.ejs +481 -0
  198. package/templates/mvc/js/src/errors/ApiError.js +14 -0
  199. package/templates/mvc/js/src/errors/BadRequestError.js +11 -0
  200. package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +22 -0
  201. package/templates/mvc/js/src/errors/NotFoundError.js +11 -0
  202. package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +22 -0
  203. package/templates/mvc/js/src/graphql/context.js.ejs +7 -0
  204. package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -0
  205. package/templates/mvc/js/src/graphql/index.js.ejs +5 -0
  206. package/templates/mvc/js/src/graphql/resolvers/index.js.ejs +6 -0
  207. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +25 -0
  208. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +64 -0
  209. package/templates/mvc/js/src/graphql/typeDefs/index.js.ejs +6 -0
  210. package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +19 -0
  211. package/templates/mvc/js/src/index.js.ejs +141 -0
  212. package/templates/mvc/js/src/routes/api.js.ejs +15 -0
  213. package/templates/mvc/js/src/routes/api.spec.js.ejs +41 -0
  214. package/templates/mvc/js/src/utils/errorMessages.js +14 -0
  215. package/templates/mvc/js/src/utils/errorMiddleware.js +29 -0
  216. package/templates/mvc/js/src/utils/httpCodes.js +9 -0
  217. package/templates/mvc/js/src/utils/logger.js +40 -0
  218. package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -0
  219. package/templates/mvc/ts/src/config/env.ts.ejs +45 -0
  220. package/templates/mvc/ts/src/config/swagger.ts.ejs +6 -0
  221. package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +481 -0
  222. package/templates/mvc/ts/src/controllers/userController.ts.ejs +292 -0
  223. package/templates/mvc/ts/src/errors/ApiError.ts +15 -0
  224. package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +22 -0
  225. package/templates/mvc/ts/src/errors/BadRequestError.ts +9 -0
  226. package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +27 -0
  227. package/templates/mvc/ts/src/errors/NotFoundError.ts +9 -0
  228. package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -0
  229. package/templates/mvc/ts/src/graphql/context.ts.ejs +12 -0
  230. package/templates/mvc/ts/src/graphql/index.ts.ejs +3 -0
  231. package/templates/mvc/ts/src/graphql/resolvers/index.ts.ejs +4 -0
  232. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -0
  233. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +29 -0
  234. package/templates/mvc/ts/src/graphql/typeDefs/index.ts.ejs +4 -0
  235. package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +17 -0
  236. package/templates/mvc/ts/src/index.ts.ejs +157 -0
  237. package/templates/mvc/ts/src/routes/api.spec.ts.ejs +59 -0
  238. package/templates/mvc/ts/src/routes/api.ts.ejs +17 -0
  239. package/templates/mvc/ts/src/utils/errorMessages.ts +12 -0
  240. package/templates/mvc/ts/src/utils/errorMiddleware.ts.ejs +27 -0
  241. package/templates/mvc/ts/src/utils/httpCodes.ts +7 -0
  242. package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +63 -0
  243. package/templates/mvc/ts/src/utils/logger.ts +36 -0
@@ -0,0 +1,148 @@
1
+ # <%= projectName %>
2
+
3
+ ![Node.js](https://img.shields.io/badge/Node.js-18%2B-green.svg)
4
+ ![License](https://img.shields.io/badge/License-ISC-blue.svg)
5
+ <% if (language === 'TypeScript') { %>![TypeScript](https://img.shields.io/badge/Language-TypeScript-blue.svg)<% } else { %>![JavaScript](https://img.shields.io/badge/Language-JavaScript-yellow.svg)<% } %>
6
+ <% if (includeSecurity) { %>
7
+ [![Snyk Vulnerabilities](https://img.shields.io/snyk/vulnerabilities/github/yourusername/<%= projectName %>?style=flat-square)](https://snyk.io/)
8
+ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=<%= projectName %>&metric=alert_status)](https://sonarcloud.io/)
9
+ <% } %>
10
+
11
+ A production-ready Node.js microservice generated with **<%= architecture %>** and **<%= database %>**.
12
+ This project follows a strict **7-Step Production-Ready Process** to ensure quality and scalability from day one.
13
+
14
+ ---
15
+
16
+ ## 🚀 7-Step Production-Ready Process
17
+
18
+ 1. **Initialize Git**: `git init` (Required for Husky hooks and security gates).
19
+ 2. **Install Dependencies**: `npm install`.
20
+ 3. **Configure Environment**: Copy `.env.example` to `.env`.
21
+ 4. **Start Infrastructure**: `docker-compose up -d<% if (database !== 'None') { %> db<% } %><% if (caching === 'Redis') { %> redis<% } %><% if (communication === 'Kafka') { %> kafka<% } %>`.
22
+ 5. **Run Development**: `npm run dev`.
23
+ 6. **Verify Standards**: `npm run lint` and `npm test` (Enforce 80% coverage).
24
+ 7. **Build & Deploy**: `npm run build` followed by `npm run deploy` (via PM2).
25
+
26
+ ---
27
+
28
+ ## 🚀 Key Features
29
+
30
+ - **Architecture**: <%= architecture %> (<% if (architecture === 'Clean Architecture') { %>Domain, UseCases, Infrastructure<% } else { %>MVC Pattern<% } %>).
31
+ - **Database**: <%= database %> <% if (database !== 'None') { %>(via <%= database === 'MongoDB' ? 'Mongoose' : 'Sequelize' %>)<% } %>.
32
+ - **Security**: Helmet, CORS, Rate Limiting, HPP, Snyk SCA.
33
+ - **Quality**: 80%+ Test Coverage, Eslint, Prettier, Husky.
34
+ - **DevOps**: Multi-stage Docker, CI/CD ready (GitHub/GitLab/Jenkins).
35
+ <% if (includeSecurity) { %>- **Enterprise Hardening**: SonarCloud SAST, Security Policies.<% } %>
36
+
37
+ ## 📂 Project Structure
38
+
39
+ The project follows **<%= architecture %>** principles.
40
+ <% if (architecture === 'Clean Architecture') { -%>
41
+ - **Domain**: Pure business logic (Entities/Interfaces).
42
+ - **Use Case**: Application-specific business rules.
43
+ - **Infrastructure**: External concerns (DB, Messaging, Caching).
44
+ <% } else { -%>
45
+ - **Model**: Database schemas and data logic.
46
+ - **View**: Template engines or API responders.
47
+ - **Controller**: Orchestrates flow between Model and View.
48
+ <% } -%>
49
+
50
+ ---
51
+
52
+ ## 🛠️ Detailed Getting Started
53
+
54
+ Follow the **🚀 7-Step Production-Ready Process** summary at the top, or follow these detailed instructions:
55
+
56
+ ### 1. Prerequisites
57
+ - Node.js (v18+)
58
+ - Docker & Docker Compose
59
+
60
+ ### 2. Environment Setup
61
+ Copy the example environment file and adjust the values as needed:
62
+ ```bash
63
+ cp .env.example .env
64
+ ```
65
+
66
+ ### 3. Infrastructure & App Launch
67
+ ```bash
68
+ # Initialize Git for security hooks
69
+ git init
70
+
71
+ # Install dependencies
72
+ npm install
73
+
74
+ # Start required services
75
+ docker-compose up -d<% if (database !== 'None') { %> db<% } %><% if (caching === 'Redis') { %> redis<% } %><% if (communication === 'Kafka') { %> kafka<% } %>
76
+
77
+ # Run the app in development mode
78
+ npm run dev
79
+ ```
80
+
81
+ ### 4. Quality & Standards
82
+ ```bash
83
+ # Lint & Format
84
+ npm run lint
85
+ npm run format
86
+
87
+ # Run Unit/Integration Tests
88
+ npm test
89
+ npm run test:coverage
90
+ ```
91
+
92
+ <% if (communication === 'Kafka') { -%>
93
+ Microservices communication handled via **Kafka**.
94
+ <% } else if (communication === 'GraphQL') { -%>
95
+ API is exposed via **GraphQL**.
96
+ The Apollo Sandbox UI for API exploration and documentation is available natively, fully embedded for offline development:
97
+ - **URL**: `http://localhost:3000/graphql` (Dynamic based on PORT)
98
+ If you are opening `http://localhost:3000/graphql` in your browser, you can directly run the following in the Apollo Sandbox UI:
99
+
100
+ **Query to get all users:**
101
+ ```graphql
102
+ query GetAllUsers {
103
+ getAllUsers {
104
+ id
105
+ name
106
+ email
107
+ }
108
+ }
109
+ ```
110
+
111
+ **Mutation to create a user:**
112
+ ```graphql
113
+ mutation CreateUser {
114
+ createUser(name: "John Doe", email: "john@example.com") {
115
+ id
116
+ name
117
+ email
118
+ }
119
+ }
120
+ ```
121
+ <% } else { -%>
122
+ API is exposed via **REST**.
123
+ A Swagger UI for API documentation is available at:
124
+ - **URL**: `http://localhost:3000/api-docs` (Dynamic based on PORT)
125
+
126
+ ### 🛣️ User Endpoints:
127
+ - `GET /api/users`: List all users.
128
+ - `POST /api/users`: Create a new user.
129
+ - `PATCH /api/users/:id`: Partially update a user.
130
+ - `DELETE /api/users/:id`: Delete a user (Soft Delete).
131
+ <%_ } -%>
132
+
133
+ ## 🐳 Docker Deployment
134
+ This project uses a **Multi-Stage Dockerfile** for optimized production images.
135
+
136
+ <% if (database !== 'None' || caching === 'Redis' || communication === 'Kafka') { -%>
137
+ ### 1. Running Locally (Development)
138
+ To run the Node.js application locally while using Docker for the infrastructure (Database, Redis, Kafka, etc.):
139
+
140
+ ```bash
141
+ # Start infrastructure
142
+ docker-compose up -d<% if (database !== 'None') { %> db<% } %><% if (caching === 'Redis') { %> redis<% } %><% if (communication === 'Kafka') { %> kafka<% } %>
143
+
144
+ # Start the application
145
+ npm run dev
146
+ ```
147
+ <% } -%>
148
+
@@ -0,0 +1,46 @@
1
+ name: Node.js CI
2
+
3
+ env:
4
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
5
+
6
+ on:
7
+ push:
8
+ branches: [ "main" ]
9
+ pull_request:
10
+ branches: [ "main" ]
11
+
12
+ jobs:
13
+ build:
14
+
15
+ runs-on: ubuntu-latest
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Use Node.js 22.x
21
+ uses: actions/setup-node@v4
22
+ with:
23
+ node-version: 22.x
24
+ cache: 'npm'
25
+
26
+ - name: Install Dependencies
27
+ run: npm ci
28
+
29
+ - name: Lint Code
30
+ run: npm run lint
31
+
32
+ - name: Run Unit Tests
33
+ run: npm run test:coverage
34
+
35
+ - name: Run E2E Tests
36
+ run: npm run test:e2e
37
+
38
+ - name: Build
39
+ run: npm run build --if-present
40
+ <% if (includeSecurity) { %>
41
+ - name: SonarQube Scan
42
+ uses: SonarSource/sonarqube-scan-action@master
43
+ env:
44
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
45
+ SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
46
+ <% } %>
@@ -0,0 +1,36 @@
1
+ name: Enterprise Security Scan
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main" ]
6
+ pull_request:
7
+ branches: [ "main" ]
8
+ schedule:
9
+ - cron: '0 0 * * 1' # Weekly scan
10
+
11
+ jobs:
12
+ node-security:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Run Snyk to check for vulnerabilities
18
+ uses: snyk/actions/node@master
19
+ env:
20
+ SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
21
+ with:
22
+ args: --severity-threshold=high
23
+
24
+ container-security:
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+ - name: Build Docker image
29
+ run: docker build -t <%= projectName %>:latest .
30
+ - name: Run Snyk to check Docker image for vulnerabilities
31
+ uses: snyk/actions/docker@master
32
+ env:
33
+ SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
34
+ with:
35
+ image: <%= projectName %>:latest
36
+ args: --file=Dockerfile --severity-threshold=high --skip-unused-projects
@@ -0,0 +1,5 @@
1
+ node_modules
2
+ dist
3
+ .env
4
+ .DS_Store
5
+ coverage
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env sh
2
+ . "$(dirname -- "$0")/_/husky.sh"
3
+
4
+ npx lint-staged
@@ -0,0 +1,29 @@
1
+ const User = require('../domain/models/User');
2
+ <%_ if (caching === 'Redis') { -%>
3
+ const cacheService = require('../infrastructure/caching/redisClient');
4
+ <%_ } else if (caching === 'Memory Cache') { -%>
5
+ const cacheService = require('../infrastructure/caching/memoryCache');
6
+ <%_ } -%>
7
+ const logger = require('../infrastructure/log/logger');
8
+
9
+ class CreateUser {
10
+ constructor(userRepository) {
11
+ this.userRepository = userRepository;
12
+ }
13
+
14
+ async execute(name, email) {
15
+ const user = new User(null, name, email);
16
+ const savedUser = await this.userRepository.save(user);
17
+
18
+ try {
19
+ await cacheService.del('users:all');
20
+ logger.info('Invalidated users:all cache');
21
+ } catch (error) {
22
+ logger.error('Cache error (del):', error);
23
+ }
24
+
25
+ return savedUser;
26
+ }
27
+ }
28
+
29
+ module.exports = CreateUser;
@@ -0,0 +1,27 @@
1
+ <%_ if (caching === 'Redis') { -%>
2
+ const cacheService = require('../infrastructure/caching/redisClient');
3
+ <%_ } else if (caching === 'Memory Cache') { -%>
4
+ const cacheService = require('../infrastructure/caching/memoryCache');
5
+ <%_ } -%>
6
+ const logger = require('../infrastructure/log/logger');
7
+
8
+ class DeleteUser {
9
+ constructor(userRepository) {
10
+ this.userRepository = userRepository;
11
+ }
12
+
13
+ async execute(id) {
14
+ const result = await this.userRepository.delete(id);
15
+
16
+ try {
17
+ await cacheService.del('users:all');
18
+ logger.info('Invalidated users:all cache');
19
+ } catch (error) {
20
+ logger.error('Cache error (del):', error);
21
+ }
22
+
23
+ return result;
24
+ }
25
+ }
26
+
27
+ module.exports = DeleteUser;
@@ -0,0 +1,37 @@
1
+ <%_ if (caching === 'Redis') { -%>
2
+ const cacheService = require('../infrastructure/caching/redisClient');
3
+ <%_ } else if (caching === 'Memory Cache') { -%>
4
+ const cacheService = require('../infrastructure/caching/memoryCache');
5
+ <%_ } -%>
6
+ const logger = require('../infrastructure/log/logger');
7
+
8
+ class GetAllUsers {
9
+ constructor(userRepository) {
10
+ this.userRepository = userRepository;
11
+ }
12
+
13
+ async execute() {
14
+ const cacheKey = 'users:all';
15
+ try {
16
+ const cachedUsers = await cacheService.get(cacheKey);
17
+ if (cachedUsers) {
18
+ logger.info('Serving users from cache');
19
+ return cachedUsers;
20
+ }
21
+ } catch (error) {
22
+ logger.error('Cache error (get):', error);
23
+ }
24
+
25
+ const users = await this.userRepository.getUsers();
26
+
27
+ try {
28
+ await cacheService.set(cacheKey, users, 60);
29
+ } catch (error) {
30
+ logger.error('Cache error (set):', error);
31
+ }
32
+
33
+ return users;
34
+ }
35
+ }
36
+
37
+ module.exports = GetAllUsers;
@@ -0,0 +1,27 @@
1
+ <%_ if (caching === 'Redis') { -%>
2
+ const cacheService = require('../infrastructure/caching/redisClient');
3
+ <%_ } else if (caching === 'Memory Cache') { -%>
4
+ const cacheService = require('../infrastructure/caching/memoryCache');
5
+ <%_ } -%>
6
+ const logger = require('../infrastructure/log/logger');
7
+
8
+ class UpdateUser {
9
+ constructor(userRepository) {
10
+ this.userRepository = userRepository;
11
+ }
12
+
13
+ async execute(id, data) {
14
+ const updatedUser = await this.userRepository.update(id, data);
15
+
16
+ try {
17
+ await cacheService.del('users:all');
18
+ logger.info('Invalidated users:all cache');
19
+ } catch (error) {
20
+ logger.error('Cache error (del):', error);
21
+ }
22
+
23
+ return updatedUser;
24
+ }
25
+ }
26
+
27
+ module.exports = UpdateUser;
@@ -0,0 +1,27 @@
1
+ import { User } from '@/domain/user';
2
+
3
+ import { UserRepository } from '@/infrastructure/repositories/UserRepository';
4
+ <%_ if (caching === 'Redis') { -%>
5
+ import cacheService from '@/infrastructure/caching/redisClient';
6
+ <%_ } else if (caching === 'Memory Cache') { -%>
7
+ import cacheService from '@/infrastructure/caching/memoryCache';
8
+ <%_ } -%>
9
+ import logger from '@/infrastructure/log/logger';
10
+
11
+ export default class CreateUser {
12
+ constructor(private userRepository: UserRepository) {}
13
+
14
+ async execute(name: string, email: string) {
15
+ const user = new User(null, name, email);
16
+ const savedUser = await this.userRepository.save(user);
17
+
18
+ try {
19
+ await cacheService.del('users:all');
20
+ logger.info('Invalidated users:all cache');
21
+ } catch (error) {
22
+ logger.error('Cache error (del):', error);
23
+ }
24
+
25
+ return savedUser;
26
+ }
27
+ }
@@ -0,0 +1,24 @@
1
+ import { UserRepository } from '@/infrastructure/repositories/UserRepository';
2
+ <%_ if (caching === 'Redis') { -%>
3
+ import cacheService from '@/infrastructure/caching/redisClient';
4
+ <%_ } else if (caching === 'Memory Cache') { -%>
5
+ import cacheService from '@/infrastructure/caching/memoryCache';
6
+ <%_ } -%>
7
+ import logger from '@/infrastructure/log/logger';
8
+
9
+ export default class DeleteUser {
10
+ constructor(private userRepository: UserRepository) {}
11
+
12
+ async execute(id: number | string) {
13
+ const result = await this.userRepository.delete(id);
14
+
15
+ try {
16
+ await cacheService.del('users:all');
17
+ logger.info('Invalidated users:all cache');
18
+ } catch (error) {
19
+ logger.error('Cache error (del):', error);
20
+ }
21
+
22
+ return result;
23
+ }
24
+ }
@@ -0,0 +1,34 @@
1
+ import { UserRepository } from '@/infrastructure/repositories/UserRepository';
2
+ <%_ if (caching === 'Redis') { -%>
3
+ import cacheService from '@/infrastructure/caching/redisClient';
4
+ <%_ } else if (caching === 'Memory Cache') { -%>
5
+ import cacheService from '@/infrastructure/caching/memoryCache';
6
+ <%_ } -%>
7
+ import logger from '@/infrastructure/log/logger';
8
+
9
+ export default class GetAllUsers {
10
+ constructor(private userRepository: UserRepository) {}
11
+
12
+ async execute() {
13
+ const cacheKey = 'users:all';
14
+ try {
15
+ const cachedUsers = await cacheService.get(cacheKey);
16
+ if (cachedUsers) {
17
+ logger.info('Serving users from cache');
18
+ return cachedUsers;
19
+ }
20
+ } catch (error) {
21
+ logger.error('Cache error (get):', error);
22
+ }
23
+
24
+ const users = await this.userRepository.getUsers();
25
+
26
+ try {
27
+ await cacheService.set(cacheKey, users, 60);
28
+ } catch (error) {
29
+ logger.error('Cache error (set):', error);
30
+ }
31
+
32
+ return users;
33
+ }
34
+ }
@@ -0,0 +1,25 @@
1
+ import { User } from '@/domain/user';
2
+ import { UserRepository } from '@/infrastructure/repositories/UserRepository';
3
+ <%_ if (caching === 'Redis') { -%>
4
+ import cacheService from '@/infrastructure/caching/redisClient';
5
+ <%_ } else if (caching === 'Memory Cache') { -%>
6
+ import cacheService from '@/infrastructure/caching/memoryCache';
7
+ <%_ } -%>
8
+ import logger from '@/infrastructure/log/logger';
9
+
10
+ export default class UpdateUser {
11
+ constructor(private userRepository: UserRepository) {}
12
+
13
+ async execute(id: number | string, data: { name?: string, email?: string }) {
14
+ const updatedUser = await this.userRepository.update(id, data);
15
+
16
+ try {
17
+ await cacheService.del('users:all');
18
+ logger.info('Invalidated users:all cache');
19
+ } catch (error) {
20
+ logger.error('Cache error (del):', error);
21
+ }
22
+
23
+ return updatedUser;
24
+ }
25
+ }
@@ -0,0 +1,60 @@
1
+ const NodeCache = require('node-cache');
2
+ const logger = require('<%- loggerPath %>');
3
+
4
+ class MemoryCacheService {
5
+ constructor() {
6
+ // Default TTL of 0 (unlimited), check period of 120s
7
+ this.cache = new NodeCache({ stdTTL: 0, checkperiod: 120 });
8
+ logger.info('Memory Cache initialized');
9
+ }
10
+
11
+ static getInstance() {
12
+ if (!MemoryCacheService.instance) {
13
+ MemoryCacheService.instance = new MemoryCacheService();
14
+ }
15
+ return MemoryCacheService.instance;
16
+ }
17
+
18
+ async get(key) {
19
+ try {
20
+ const data = this.cache.get(key);
21
+ if (data === undefined) return null;
22
+ return typeof data === 'string' ? JSON.parse(data) : data;
23
+ } catch (error) {
24
+ logger.error(`Memory Cache get error for key ${key}:`, error);
25
+ return null;
26
+ }
27
+ }
28
+
29
+ async set(key, value, ttl) {
30
+ try {
31
+ const data = JSON.stringify(value);
32
+ if (ttl) {
33
+ this.cache.set(key, data, ttl);
34
+ } else {
35
+ this.cache.set(key, data);
36
+ }
37
+ } catch (error) {
38
+ logger.error(`Memory Cache set error for key ${key}:`, error);
39
+ }
40
+ }
41
+
42
+ async del(key) {
43
+ try {
44
+ this.cache.del(key);
45
+ } catch (error) {
46
+ logger.error(`Memory Cache del error for key ${key}:`, error);
47
+ }
48
+ }
49
+
50
+ async getOrSet(key, fetcher, ttl = 3600) {
51
+ const cached = await this.get(key);
52
+ if (cached) return cached;
53
+
54
+ const data = await fetcher();
55
+ if (data) await this.set(key, data, ttl);
56
+ return data;
57
+ }
58
+ }
59
+
60
+ module.exports = MemoryCacheService.getInstance();
@@ -0,0 +1,101 @@
1
+ jest.mock('node-cache', () => {
2
+ return jest.fn().mockImplementation(() => ({
3
+ get: jest.fn(),
4
+ set: jest.fn(),
5
+ del: jest.fn(),
6
+ }));
7
+ });
8
+
9
+ jest.mock('<%- loggerPath %>', () => ({
10
+ info: jest.fn(),
11
+ error: jest.fn(),
12
+ }));
13
+
14
+ describe('Memory Cache Client', () => {
15
+ let memoryCacheService;
16
+
17
+ beforeEach(() => {
18
+ jest.clearAllMocks();
19
+ jest.resetModules();
20
+ memoryCacheService = require('<% if (architecture === "MVC") { %>@/config/memoryCache<% } else { %>@/infrastructure/caching/memoryCache<% } %>');
21
+ });
22
+
23
+ it('should get data from memory cache', async () => {
24
+ const mockData = { test: 'data' };
25
+ memoryCacheService.cache.get.mockReturnValue(JSON.stringify(mockData));
26
+
27
+ const data = await memoryCacheService.get('test-key');
28
+ expect(data).toEqual(mockData);
29
+ expect(memoryCacheService.cache.get).toHaveBeenCalledWith('test-key');
30
+ });
31
+
32
+ it('should return null when key is not found', async () => {
33
+ memoryCacheService.cache.get.mockReturnValue(undefined);
34
+ const data = await memoryCacheService.get('non-existent');
35
+ expect(data).toBeNull();
36
+ });
37
+
38
+ it('should return data as is if not a string', async () => {
39
+ const mockData = { test: 'data' };
40
+ memoryCacheService.cache.get.mockReturnValue(mockData);
41
+ const data = await memoryCacheService.get('binary-key');
42
+ expect(data).toEqual(mockData);
43
+ });
44
+
45
+ it('should handle errors in get', async () => {
46
+ memoryCacheService.cache.get.mockImplementation(() => { throw new Error('Cache Error'); });
47
+ const data = await memoryCacheService.get('error-key');
48
+ expect(data).toBeNull();
49
+ });
50
+
51
+ it('should set data to memory cache without TTL', async () => {
52
+ const mockData = { test: 'data' };
53
+ await memoryCacheService.set('test-key', mockData);
54
+ expect(memoryCacheService.cache.set).toHaveBeenCalledWith('test-key', JSON.stringify(mockData));
55
+ });
56
+
57
+ it('should handle errors in set', async () => {
58
+ memoryCacheService.cache.set.mockImplementation(() => { throw new Error('Cache Error'); });
59
+ await memoryCacheService.set('test-key', 'value');
60
+ });
61
+
62
+ it('should handle errors in del', async () => {
63
+ memoryCacheService.cache.del.mockImplementation(() => { throw new Error('Cache Error'); });
64
+ await memoryCacheService.del('test-key');
65
+ });
66
+
67
+ it('should use getOrSet and call fetcher if not cached', async () => {
68
+ memoryCacheService.get = jest.fn().mockResolvedValue(null);
69
+ memoryCacheService.set = jest.fn();
70
+ const fetcher = jest.fn().mockResolvedValue({ new: 'data' });
71
+
72
+ const result = await memoryCacheService.getOrSet('new-key', fetcher);
73
+
74
+ expect(result).toEqual({ new: 'data' });
75
+ expect(fetcher).toHaveBeenCalled();
76
+ // Result should be cached
77
+ expect(memoryCacheService.set).toHaveBeenCalledWith('new-key', { new: 'data' }, 3600);
78
+ });
79
+
80
+ it('should return cached data in getOrSet', async () => {
81
+ const cachedData = { cached: 'data' };
82
+ memoryCacheService.get = jest.fn().mockResolvedValue(cachedData);
83
+ const fetcher = jest.fn();
84
+
85
+ const result = await memoryCacheService.getOrSet('cached-key', fetcher);
86
+
87
+ expect(result).toEqual(cachedData);
88
+ expect(fetcher).not.toHaveBeenCalled();
89
+ });
90
+
91
+ it('should handle falsy data from fetcher in getOrSet', async () => {
92
+ memoryCacheService.get = jest.fn().mockResolvedValue(null);
93
+ memoryCacheService.set = jest.fn();
94
+ const fetcher = jest.fn().mockResolvedValue(null);
95
+
96
+ const result = await memoryCacheService.getOrSet('empty-key', fetcher);
97
+
98
+ expect(result).toBeNull();
99
+ expect(memoryCacheService.set).not.toHaveBeenCalled();
100
+ });
101
+ });