nodejs-quickstart-structure 1.18.1 → 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.
Files changed (235) hide show
  1. package/CHANGELOG.md +309 -294
  2. package/LICENSE +15 -15
  3. package/README.md +2 -1
  4. package/lib/generator.js +139 -139
  5. package/lib/modules/app-setup.js +401 -401
  6. package/lib/modules/caching-setup.js +76 -73
  7. package/lib/modules/config-files.js +151 -151
  8. package/lib/modules/database-setup.js +116 -116
  9. package/lib/modules/kafka-setup.js +249 -191
  10. package/lib/modules/project-setup.js +32 -31
  11. package/lib/prompts.js +100 -100
  12. package/package.json +78 -67
  13. package/templates/clean-architecture/js/src/domain/models/User.js +9 -9
  14. package/templates/clean-architecture/js/src/errors/ApiError.js +14 -14
  15. package/templates/clean-architecture/js/src/errors/BadRequestError.js +11 -10
  16. package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +22 -21
  17. package/templates/clean-architecture/js/src/errors/NotFoundError.js +11 -10
  18. package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +22 -21
  19. package/templates/clean-architecture/js/src/index.js.ejs +55 -55
  20. package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +47 -47
  21. package/templates/clean-architecture/js/src/infrastructure/log/logger.js +36 -36
  22. package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -63
  23. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +69 -39
  24. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +142 -81
  25. package/templates/clean-architecture/js/src/infrastructure/webserver/middleware/errorMiddleware.js +30 -30
  26. package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +89 -89
  27. package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.js.ejs +6 -6
  28. package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +156 -75
  29. package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +234 -138
  30. package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -13
  31. package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -31
  32. package/templates/clean-architecture/js/src/interfaces/graphql/index.js.ejs +5 -5
  33. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/index.js.ejs +6 -6
  34. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +27 -21
  35. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +66 -49
  36. package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/index.js.ejs +6 -6
  37. package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +19 -17
  38. package/templates/clean-architecture/js/src/interfaces/routes/api.js +12 -10
  39. package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -38
  40. package/templates/clean-architecture/js/src/usecases/CreateUser.js +14 -14
  41. package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -51
  42. package/templates/clean-architecture/js/src/usecases/DeleteUser.js +11 -0
  43. package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +47 -0
  44. package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +12 -12
  45. package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -61
  46. package/templates/clean-architecture/js/src/usecases/UpdateUser.js +11 -0
  47. package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +48 -0
  48. package/templates/clean-architecture/js/src/utils/errorMessages.js +14 -0
  49. package/templates/clean-architecture/js/src/utils/httpCodes.js +9 -9
  50. package/templates/clean-architecture/ts/src/config/env.ts.ejs +46 -46
  51. package/templates/clean-architecture/ts/src/config/swagger.ts.ejs +6 -6
  52. package/templates/clean-architecture/ts/src/domain/user.ts +7 -7
  53. package/templates/clean-architecture/ts/src/errors/ApiError.ts +15 -15
  54. package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +22 -21
  55. package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +9 -8
  56. package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +22 -21
  57. package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +9 -8
  58. package/templates/clean-architecture/ts/src/index.ts.ejs +139 -139
  59. package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +63 -63
  60. package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +36 -36
  61. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +175 -85
  62. package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +74 -0
  63. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +331 -185
  64. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +173 -84
  65. package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -32
  66. package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +17 -17
  67. package/templates/clean-architecture/ts/src/interfaces/graphql/index.ts.ejs +3 -3
  68. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/index.ts.ejs +4 -4
  69. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -51
  70. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +29 -21
  71. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/index.ts.ejs +4 -4
  72. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +17 -15
  73. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -40
  74. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +13 -11
  75. package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -51
  76. package/templates/clean-architecture/ts/src/usecases/createUser.ts +13 -13
  77. package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +47 -0
  78. package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +9 -0
  79. package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -63
  80. package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +10 -10
  81. package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +48 -0
  82. package/templates/clean-architecture/ts/src/usecases/updateUser.ts +9 -0
  83. package/templates/clean-architecture/ts/src/utils/errorMessages.ts +12 -0
  84. package/templates/clean-architecture/ts/src/utils/errorMiddleware.ts.ejs +27 -27
  85. package/templates/clean-architecture/ts/src/utils/httpCodes.ts +7 -7
  86. package/templates/common/.cursorrules.ejs +60 -60
  87. package/templates/common/.dockerignore +12 -12
  88. package/templates/common/.env.example.ejs +41 -41
  89. package/templates/common/.gitattributes +46 -0
  90. package/templates/common/.gitlab-ci.yml.ejs +86 -86
  91. package/templates/common/.lintstagedrc +6 -6
  92. package/templates/common/.prettierrc +7 -7
  93. package/templates/common/Dockerfile +73 -73
  94. package/templates/common/Jenkinsfile.ejs +87 -87
  95. package/templates/common/README.md.ejs +294 -270
  96. package/templates/common/SECURITY.md +20 -20
  97. package/templates/common/_github/workflows/ci.yml.ejs +46 -46
  98. package/templates/common/_github/workflows/security.yml.ejs +36 -36
  99. package/templates/common/_gitignore +5 -5
  100. package/templates/common/_husky/pre-commit +4 -4
  101. package/templates/common/caching/clean/js/CreateUser.js.ejs +29 -29
  102. package/templates/common/caching/clean/js/DeleteUser.js.ejs +27 -0
  103. package/templates/common/caching/clean/js/GetAllUsers.js.ejs +37 -37
  104. package/templates/common/caching/clean/js/UpdateUser.js.ejs +27 -0
  105. package/templates/common/caching/clean/ts/createUser.ts.ejs +27 -27
  106. package/templates/common/caching/clean/ts/deleteUser.ts.ejs +24 -0
  107. package/templates/common/caching/clean/ts/getAllUsers.ts.ejs +34 -34
  108. package/templates/common/caching/clean/ts/updateUser.ts.ejs +25 -0
  109. package/templates/common/caching/js/memoryCache.js.ejs +60 -60
  110. package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -101
  111. package/templates/common/caching/js/redisClient.js.ejs +75 -75
  112. package/templates/common/caching/js/redisClient.spec.js.ejs +147 -147
  113. package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -102
  114. package/templates/common/caching/ts/memoryCache.ts.ejs +73 -64
  115. package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -157
  116. package/templates/common/caching/ts/redisClient.ts.ejs +89 -80
  117. package/templates/common/database/js/database.js.ejs +19 -19
  118. package/templates/common/database/js/database.spec.js.ejs +56 -56
  119. package/templates/common/database/js/models/User.js.ejs +79 -53
  120. package/templates/common/database/js/models/User.js.mongoose.ejs +23 -19
  121. package/templates/common/database/js/models/User.spec.js.ejs +94 -84
  122. package/templates/common/database/js/mongoose.js.ejs +33 -33
  123. package/templates/common/database/js/mongoose.spec.js.ejs +43 -43
  124. package/templates/common/database/ts/database.spec.ts.ejs +56 -56
  125. package/templates/common/database/ts/database.ts.ejs +21 -21
  126. package/templates/common/database/ts/models/User.spec.ts.ejs +100 -84
  127. package/templates/common/database/ts/models/User.ts.ejs +87 -61
  128. package/templates/common/database/ts/models/User.ts.mongoose.ejs +30 -25
  129. package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -42
  130. package/templates/common/database/ts/mongoose.ts.ejs +28 -28
  131. package/templates/common/docker-compose.yml.ejs +159 -159
  132. package/templates/common/ecosystem.config.js.ejs +40 -40
  133. package/templates/common/eslint.config.mjs.ejs +77 -77
  134. package/templates/common/health/js/healthRoute.js.ejs +50 -47
  135. package/templates/common/health/js/healthRoute.spec.js.ejs +70 -70
  136. package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -76
  137. package/templates/common/health/ts/healthRoute.ts.ejs +49 -46
  138. package/templates/common/jest.config.js.ejs +32 -32
  139. package/templates/common/jest.e2e.config.js.ejs +8 -8
  140. package/templates/common/kafka/js/config/kafka.js +9 -9
  141. package/templates/common/kafka/js/config/kafka.spec.js.ejs +27 -27
  142. package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +30 -30
  143. package/templates/common/kafka/js/messaging/baseConsumer.spec.js.ejs +58 -58
  144. package/templates/common/kafka/js/messaging/userEventSchema.js.ejs +12 -11
  145. package/templates/common/kafka/js/messaging/userEventSchema.spec.js.ejs +27 -27
  146. package/templates/common/kafka/js/messaging/welcomeEmailConsumer.js.ejs +44 -31
  147. package/templates/common/kafka/js/messaging/welcomeEmailConsumer.spec.js.ejs +86 -49
  148. package/templates/common/kafka/js/services/kafkaService.js.ejs +93 -93
  149. package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +106 -106
  150. package/templates/common/kafka/js/utils/kafkaEvents.js.ejs +7 -0
  151. package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +27 -27
  152. package/templates/common/kafka/ts/config/kafka.ts +7 -7
  153. package/templates/common/kafka/ts/messaging/baseConsumer.spec.ts.ejs +50 -50
  154. package/templates/common/kafka/ts/messaging/baseConsumer.ts.ejs +27 -27
  155. package/templates/common/kafka/ts/messaging/userEventSchema.spec.ts.ejs +51 -51
  156. package/templates/common/kafka/ts/messaging/userEventSchema.ts.ejs +12 -11
  157. package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.spec.ts.ejs +86 -49
  158. package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.ts.ejs +38 -25
  159. package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +81 -81
  160. package/templates/common/kafka/ts/services/kafkaService.ts.ejs +95 -95
  161. package/templates/common/kafka/ts/utils/kafkaEvents.ts.ejs +5 -0
  162. package/templates/common/migrate-mongo-config.js.ejs +31 -31
  163. package/templates/common/migrations/init.js.ejs +23 -23
  164. package/templates/common/package.json.ejs +119 -118
  165. package/templates/common/prompts/add-feature.md.ejs +26 -26
  166. package/templates/common/prompts/project-context.md.ejs +43 -43
  167. package/templates/common/prompts/troubleshoot.md.ejs +28 -28
  168. package/templates/common/public/css/style.css +147 -147
  169. package/templates/common/scripts/run-e2e.js.ejs +63 -63
  170. package/templates/common/shutdown/js/gracefulShutdown.js.ejs +65 -61
  171. package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +149 -160
  172. package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +179 -158
  173. package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +59 -55
  174. package/templates/common/sonar-project.properties.ejs +27 -27
  175. package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +120 -49
  176. package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +120 -49
  177. package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -79
  178. package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -94
  179. package/templates/common/swagger.yml.ejs +118 -66
  180. package/templates/common/tsconfig.json +22 -22
  181. package/templates/common/views/ejs/index.ejs +55 -55
  182. package/templates/common/views/pug/index.pug +40 -40
  183. package/templates/db/mysql/V1__Initial_Setup.sql.ejs +10 -9
  184. package/templates/db/postgres/V1__Initial_Setup.sql.ejs +10 -9
  185. package/templates/mvc/js/src/config/env.js.ejs +46 -46
  186. package/templates/mvc/js/src/config/swagger.js.ejs +6 -6
  187. package/templates/mvc/js/src/controllers/userController.js.ejs +246 -105
  188. package/templates/mvc/js/src/controllers/userController.spec.js.ejs +481 -209
  189. package/templates/mvc/js/src/errors/ApiError.js +14 -14
  190. package/templates/mvc/js/src/errors/BadRequestError.js +11 -10
  191. package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +22 -21
  192. package/templates/mvc/js/src/errors/NotFoundError.js +11 -10
  193. package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +22 -21
  194. package/templates/mvc/js/src/graphql/context.js.ejs +7 -7
  195. package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -29
  196. package/templates/mvc/js/src/graphql/index.js.ejs +5 -5
  197. package/templates/mvc/js/src/graphql/resolvers/index.js.ejs +6 -6
  198. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +25 -19
  199. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +64 -47
  200. package/templates/mvc/js/src/graphql/typeDefs/index.js.ejs +6 -6
  201. package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +19 -17
  202. package/templates/mvc/js/src/index.js.ejs +136 -136
  203. package/templates/mvc/js/src/routes/api.js +10 -8
  204. package/templates/mvc/js/src/routes/api.spec.js.ejs +41 -36
  205. package/templates/mvc/js/src/utils/errorMessages.js +14 -0
  206. package/templates/mvc/js/src/utils/errorMiddleware.js +29 -29
  207. package/templates/mvc/js/src/utils/httpCodes.js +9 -9
  208. package/templates/mvc/js/src/utils/logger.js +40 -40
  209. package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -63
  210. package/templates/mvc/ts/src/config/env.ts.ejs +45 -45
  211. package/templates/mvc/ts/src/config/swagger.ts.ejs +6 -6
  212. package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +481 -203
  213. package/templates/mvc/ts/src/controllers/userController.ts.ejs +248 -107
  214. package/templates/mvc/ts/src/errors/ApiError.ts +15 -15
  215. package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +22 -21
  216. package/templates/mvc/ts/src/errors/BadRequestError.ts +9 -8
  217. package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +27 -21
  218. package/templates/mvc/ts/src/errors/NotFoundError.ts +9 -8
  219. package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -30
  220. package/templates/mvc/ts/src/graphql/context.ts.ejs +12 -12
  221. package/templates/mvc/ts/src/graphql/index.ts.ejs +3 -3
  222. package/templates/mvc/ts/src/graphql/resolvers/index.ts.ejs +4 -4
  223. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -51
  224. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +29 -21
  225. package/templates/mvc/ts/src/graphql/typeDefs/index.ts.ejs +4 -4
  226. package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +17 -15
  227. package/templates/mvc/ts/src/index.ts.ejs +156 -153
  228. package/templates/mvc/ts/src/routes/api.spec.ts.ejs +59 -40
  229. package/templates/mvc/ts/src/routes/api.ts +12 -10
  230. package/templates/mvc/ts/src/utils/errorMessages.ts +12 -0
  231. package/templates/mvc/ts/src/utils/errorMiddleware.ts.ejs +27 -27
  232. package/templates/mvc/ts/src/utils/httpCodes.ts +7 -7
  233. package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +63 -63
  234. package/templates/mvc/ts/src/utils/logger.ts +36 -36
  235. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.ts.ejs +0 -37
@@ -1,51 +1,68 @@
1
- import { userResolvers } from '@/graphql/resolvers/user.resolvers';
2
-
3
- const mockGetUsers = jest.fn().mockResolvedValue([{ id: '1', name: 'John Doe', email: 'john@example.com' }]);
4
- const mockCreateUser = jest.fn().mockResolvedValue({ id: '1', name: 'Jane', email: 'jane@example.com' });
5
-
6
- jest.mock('@/controllers/userController', () => {
7
- return {
8
- UserController: jest.fn().mockImplementation(() => ({
9
- getUsers: (...args: unknown[]) => mockGetUsers(...args),
10
- createUser: (...args: unknown[]) => mockCreateUser(...args)
11
- }))
12
- };
13
- });
14
-
15
- describe('User Resolvers', () => {
16
- afterEach(() => {
17
- jest.clearAllMocks();
18
- });
19
-
20
- describe('Query.getAllUsers', () => {
21
- it('should return all users', async () => {
22
- const result = await userResolvers.Query.getAllUsers();
23
- expect(result).toEqual([{ id: '1', name: 'John Doe', email: 'john@example.com' }]);
24
- expect(mockGetUsers).toHaveBeenCalledTimes(1);
25
- });
26
- });
27
-
28
- describe('Mutation.createUser', () => {
29
- it('should create and return a new user', async () => {
30
- const result = await userResolvers.Mutation.createUser(null, { name: 'Jane', email: 'jane@example.com' });
31
- expect(result).toEqual({ id: '1', name: 'Jane', email: 'jane@example.com' });
32
- expect(mockCreateUser).toHaveBeenCalledWith({ name: 'Jane', email: 'jane@example.com' });
33
- expect(mockCreateUser).toHaveBeenCalledTimes(1);
34
- });
35
- });
36
- <%_ if (database === 'MongoDB') { -%>
37
- describe('User.id', () => {
38
- it('should return parent.id if available', () => {
39
- const parent = { id: '123' };
40
- const result = userResolvers.User.id(parent as { id?: string; _id?: unknown });
41
- expect(result).toBe('123');
42
- });
43
-
44
- it('should fallback to parent._id if id is not available', () => {
45
- const parent = { _id: '456' };
46
- const result = userResolvers.User.id(parent as { id?: string; _id?: unknown });
47
- expect(result).toBe('456');
48
- });
49
- });
50
- <%_ } -%>
51
- });
1
+ import { userResolvers } from '@/graphql/resolvers/user.resolvers';
2
+
3
+ const mockGetUsers = jest.fn().mockResolvedValue([{ id: '1', name: 'John Doe', email: 'john@example.com' }]);
4
+ const mockCreateUser = jest.fn().mockResolvedValue({ id: '1', name: 'Jane', email: 'jane@example.com' });
5
+
6
+ jest.mock('@/controllers/userController', () => {
7
+ return {
8
+ UserController: jest.fn().mockImplementation(() => ({
9
+ getUsers: (...args: unknown[]) => mockGetUsers(...args),
10
+ createUser: (...args: unknown[]) => mockCreateUser(...args),
11
+ updateUser: jest.fn().mockImplementation((id, data) => Promise.resolve({ id, ...data })),
12
+ deleteUser: jest.fn().mockImplementation(() => Promise.resolve(true))
13
+ }))
14
+ };
15
+ });
16
+
17
+ describe('User Resolvers', () => {
18
+ afterEach(() => {
19
+ jest.clearAllMocks();
20
+ });
21
+
22
+ describe('Query.getAllUsers', () => {
23
+ it('should return all users', async () => {
24
+ const result = await userResolvers.Query.getAllUsers();
25
+ expect(result).toEqual([{ id: '1', name: 'John Doe', email: 'john@example.com' }]);
26
+ expect(mockGetUsers).toHaveBeenCalledTimes(1);
27
+ });
28
+ });
29
+
30
+ describe('Mutation.createUser', () => {
31
+ it('should create and return a new user', async () => {
32
+ const result = await userResolvers.Mutation.createUser(null, { name: 'Jane', email: 'jane@example.com' });
33
+ expect(result).toEqual({ id: '1', name: 'Jane', email: 'jane@example.com' });
34
+ expect(mockCreateUser).toHaveBeenCalledWith({ name: 'Jane', email: 'jane@example.com' });
35
+ expect(mockCreateUser).toHaveBeenCalledTimes(1);
36
+ });
37
+ });
38
+
39
+ describe('Mutation.updateUser', () => {
40
+ it('should update and return the user', async () => {
41
+ const payload = { name: 'Updated' };
42
+ const result = await userResolvers.Mutation.updateUser(null, { id: '1', ...payload });
43
+ expect(result).toMatchObject(payload);
44
+ });
45
+ });
46
+
47
+ describe('Mutation.deleteUser', () => {
48
+ it('should delete and return true', async () => {
49
+ const result = await userResolvers.Mutation.deleteUser(null, { id: '1' });
50
+ expect(result).toBe(true);
51
+ });
52
+ });
53
+ <%_ if (database === 'MongoDB') { -%>
54
+ describe('User.id', () => {
55
+ it('should return parent.id if available', () => {
56
+ const parent = { id: '123' };
57
+ const result = userResolvers.User.id(parent as { id?: string; _id?: unknown });
58
+ expect(result).toBe('123');
59
+ });
60
+
61
+ it('should fallback to parent._id if id is not available', () => {
62
+ const parent = { _id: '456' };
63
+ const result = userResolvers.User.id(parent as { id?: string; _id?: unknown });
64
+ expect(result).toBe('456');
65
+ });
66
+ });
67
+ <%_ } -%>
68
+ });
@@ -1,21 +1,29 @@
1
- import { UserController } from '@/controllers/userController';
2
-
3
- const userController = new UserController();
4
-
5
- export const userResolvers = {
6
- Query: {
7
- getAllUsers: async () => {
8
- const users = await userController.getUsers();
9
- return users;
10
- }
11
- },
12
- Mutation: {
13
- createUser: async (_: unknown, { name, email }: { name: string, email: string }) => {
14
- const user = await userController.createUser({ name, email });
15
- return user;
16
- }
17
- }<%_ if (database === 'MongoDB') { -%>,
18
- User: {
19
- id: (parent: { id?: string; _id?: unknown }) => parent.id || parent._id
20
- }<%_ } %>
21
- };
1
+ import { UserController } from '@/controllers/userController';
2
+
3
+ const userController = new UserController();
4
+
5
+ export const userResolvers = {
6
+ Query: {
7
+ getAllUsers: async () => {
8
+ const users = await userController.getUsers();
9
+ return users;
10
+ }
11
+ },
12
+ Mutation: {
13
+ createUser: async (_: unknown, { name, email }: { name: string, email: string }) => {
14
+ const user = await userController.createUser({ name, email });
15
+ return user;
16
+ },
17
+ updateUser: async (_: unknown, { id, name, email }: { id: string, name?: string, email?: string }) => {
18
+ const user = await userController.updateUser(id, { name, email });
19
+ return user;
20
+ },
21
+ deleteUser: async (_: unknown, { id }: { id: string }) => {
22
+ const result = await userController.deleteUser(id);
23
+ return result;
24
+ }
25
+ }<%_ if (database === 'MongoDB') { -%>,
26
+ User: {
27
+ id: (parent: { id?: string; _id?: unknown }) => parent.id || parent._id
28
+ }<%_ } %>
29
+ };
@@ -1,4 +1,4 @@
1
- import { mergeTypeDefs } from '@graphql-tools/merge';
2
- import { userTypes } from '@/graphql/typeDefs/user.types';
3
-
4
- export const typeDefs = mergeTypeDefs([userTypes]);
1
+ import { mergeTypeDefs } from '@graphql-tools/merge';
2
+ import { userTypes } from '@/graphql/typeDefs/user.types';
3
+
4
+ export const typeDefs = mergeTypeDefs([userTypes]);
@@ -1,15 +1,17 @@
1
- export const userTypes = `#graphql
2
- type User {
3
- id: ID!
4
- name: String!
5
- email: String!
6
- }
7
-
8
- type Query {
9
- getAllUsers: [User]
10
- }
11
-
12
- type Mutation {
13
- createUser(name: String!, email: String!): User
14
- }
15
- `;
1
+ export const userTypes = `#graphql
2
+ type User {
3
+ id: ID!
4
+ name: String!
5
+ email: String!
6
+ }
7
+
8
+ type Query {
9
+ getAllUsers: [User]
10
+ }
11
+
12
+ type Mutation {
13
+ createUser(name: String!, email: String!): User
14
+ updateUser(id: ID!, name: String, email: String): User
15
+ deleteUser(id: ID!): Boolean
16
+ }
17
+ `;
@@ -1,154 +1,157 @@
1
- import { env } from '@/config/env';
2
- import express, { Request, Response } from 'express';
3
- import cors from 'cors';
4
- import helmet from 'helmet';
5
- import hpp from 'hpp';
6
- import rateLimit from 'express-rate-limit';
7
- import logger from '@/utils/logger';
8
- import morgan from 'morgan';
9
- import { errorMiddleware } from '@/utils/errorMiddleware';
10
- import { setupGracefulShutdown } from '@/utils/gracefulShutdown';
11
- import healthRoutes from '@/routes/healthRoute';
12
- <%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
13
- import apiRoutes from '@/routes/api';
14
- import swaggerUi from 'swagger-ui-express';
15
- import swaggerSpecs from '@/config/swagger';<%_ } %>
16
- <%_ if (communication === 'Kafka') { -%>import { kafkaService } from '@/services/kafkaService';<%_ } -%>
17
- <%_ if (communication === 'GraphQL') { -%>
18
- import { ApolloServer } from '@apollo/server';
19
- import { expressMiddleware } from '@as-integrations/express4';
20
- import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';
21
- import { unwrapResolverError } from '@apollo/server/errors';
22
- import { ApiError } from '@/errors/ApiError';
23
- import { typeDefs, resolvers } from '@/graphql';
24
- import { gqlContext, MyContext } from '@/graphql/context';
25
- <% } -%>
26
-
27
- const app = express();
28
- const port = env.PORT;
29
-
30
- // Security Middleware
31
- <%_ if (communication === 'GraphQL') { -%>
32
- app.use(helmet({
33
- crossOriginEmbedderPolicy: false,
34
- contentSecurityPolicy: {
35
- directives: {
36
- imgSrc: [`'self'`, 'data:', 'apollo-server-landing-page.cdn.apollographql.com'],
37
- scriptSrc: [`'self'`, `https: 'unsafe-inline'`],
38
- manifestSrc: [`'self'`, 'apollo-server-landing-page.cdn.apollographql.com'],
39
- frameSrc: [`'self'`, 'sandbox.embed.apollographql.com'],
40
- },
41
- },
42
- }));
43
- <%_ } else { -%>
44
- app.use(helmet());
45
- <%_ } -%>
46
- app.use(hpp());
47
- app.use(cors({ origin: '*', methods: ['GET', 'POST', 'PUT', 'DELETE'] }));
48
- const limiter = rateLimit({ windowMs: 10 * 60 * 1000, max: 100 });
49
- app.use(limiter);
50
-
51
- app.use(express.json());
52
- app.use(morgan('combined', { stream: { write: (message) => logger.info(message.trim()) } }));
53
- <%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
54
- app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpecs));
55
- <%_ } -%>
56
- <%_ if (viewEngine === 'EJS' || viewEngine === 'Pug') { -%>
57
- // View Engine Setup
58
- import path from 'path';
59
- app.set('views', path.join(__dirname, 'views'));
60
- app.set('view engine', '<%= viewEngine.toLowerCase() %>');
61
- app.use(express.static(path.join(__dirname, '../public')));<%_ } %>
62
- <%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
63
- app.use('/api', apiRoutes);
64
- <%_ } -%><% if (viewEngine && viewEngine !== 'None') { -%>
65
- app.get('/', (req: Request, res: Response) => {
66
- res.render('index', {
67
- projectName: 'NodeJS Service',
68
- architecture: 'MVC',
69
- database: '<%= database %>',
70
- communication: '<%= communication %>'
71
- });
72
- });
73
- <% } -%>
74
- app.use('/health', healthRoutes);
75
-
76
- // Start Server Logic
77
- const startServer = async () => {
78
- <%_ if (communication === 'GraphQL') { -%>
79
- // GraphQL Setup
80
- const apolloServer = new ApolloServer<MyContext>({
81
- typeDefs,
82
- resolvers,
83
- plugins: [ApolloServerPluginLandingPageLocalDefault({ embed: true })],
84
- formatError: (formattedError, error) => {
85
- const originalError = unwrapResolverError(error);
86
- if (originalError instanceof ApiError) {
87
- return {
88
- ...formattedError,
89
- message: originalError.message,
90
- extensions: {
91
- ...formattedError.extensions,
92
- code: originalError.statusCode.toString(),
93
- }
94
- };
95
- }
96
-
97
- logger.error(`GraphQL Error: ${formattedError.message}`);
98
- if (originalError instanceof Error && originalError.stack && process.env.NODE_ENV === 'development') {
99
- logger.error(originalError.stack);
100
- }
101
- return formattedError;
102
- },
103
- });
104
- await apolloServer.start();
105
- app.use('/graphql', expressMiddleware(apolloServer, { context: gqlContext }));
106
- <%_ } -%>
107
- app.use(errorMiddleware);
108
- const server = app.listen(port, () => {
109
- logger.info(`Server running on port ${port}`);
110
- <%_ if (communication === 'Kafka') { -%>
111
- kafkaService.connect()
112
- .then(async () => {
113
- logger.info('Kafka connected');
114
- })
115
- .catch(err => {
116
- logger.error('Failed to connect to Kafka after retries:', (err as Error).message);
117
- });
118
- <%_ } -%>
119
- });
120
-
121
- setupGracefulShutdown(server<% if(communication === 'Kafka') { %>, kafkaService<% } %>);
122
- };
123
-
124
- <%_ if (database !== 'None') { -%>
125
- // Database Sync
126
- <%_ if (database !== 'None') { -%>
127
- import <% if (database === 'MongoDB') { %>connectDB<% } else { %>sequelize<% } %> from '@/config/database';
128
- <%_ } -%>
129
- const syncDatabase = async () => {
130
- let retries = 30;
131
- while (retries) {
132
- try {
133
- <%_ if (database === 'MongoDB') { -%>
134
- await connectDB();
135
- <%_ } else { -%>
136
- await sequelize.sync();
137
- <%_ } -%>
138
- logger.info('Database synced');
139
- // Start Server after DB is ready
140
- await startServer();
141
- break;
142
- } catch (error) {
143
- logger.error('Error syncing database:', error);
144
- retries -= 1;
145
- logger.info(`Retries left: ${retries}`);
146
- await new Promise(res => setTimeout(res, 5000));
147
- }
148
- }
149
- };
150
-
151
- syncDatabase();
152
- <%_ } else { -%>
153
- startServer();
1
+ import { env } from '@/config/env';
2
+ import express from 'express';
3
+ <% if (viewEngine && viewEngine !== 'None') { -%>
4
+ import type { Request, Response } from 'express';
5
+ <% } -%>
6
+ import cors from 'cors';
7
+ import helmet from 'helmet';
8
+ import hpp from 'hpp';
9
+ import rateLimit from 'express-rate-limit';
10
+ import logger from '@/utils/logger';
11
+ import morgan from 'morgan';
12
+ import { errorMiddleware } from '@/utils/errorMiddleware';
13
+ import { setupGracefulShutdown } from '@/utils/gracefulShutdown';
14
+ import healthRoutes from '@/routes/healthRoute';
15
+ <%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
16
+ import apiRoutes from '@/routes/api';
17
+ import swaggerUi from 'swagger-ui-express';
18
+ import swaggerSpecs from '@/config/swagger';<%_ } %>
19
+ <%_ if (communication === 'Kafka') { -%>import { kafkaService } from '@/services/kafkaService';<%_ } -%>
20
+ <%_ if (communication === 'GraphQL') { -%>
21
+ import { ApolloServer } from '@apollo/server';
22
+ import { expressMiddleware } from '@as-integrations/express4';
23
+ import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';
24
+ import { unwrapResolverError } from '@apollo/server/errors';
25
+ import { ApiError } from '@/errors/ApiError';
26
+ import { typeDefs, resolvers } from '@/graphql';
27
+ import { gqlContext, MyContext } from '@/graphql/context';
28
+ <% } -%>
29
+
30
+ const app = express();
31
+ const port = env.PORT;
32
+
33
+ // Security Middleware
34
+ <%_ if (communication === 'GraphQL') { -%>
35
+ app.use(helmet({
36
+ crossOriginEmbedderPolicy: false,
37
+ contentSecurityPolicy: {
38
+ directives: {
39
+ imgSrc: [`'self'`, 'data:', 'apollo-server-landing-page.cdn.apollographql.com'],
40
+ scriptSrc: [`'self'`, `https: 'unsafe-inline'`],
41
+ manifestSrc: [`'self'`, 'apollo-server-landing-page.cdn.apollographql.com'],
42
+ frameSrc: [`'self'`, 'sandbox.embed.apollographql.com'],
43
+ },
44
+ },
45
+ }));
46
+ <%_ } else { -%>
47
+ app.use(helmet());
48
+ <%_ } -%>
49
+ app.use(hpp());
50
+ app.use(cors({ origin: '*', methods: ['GET', 'POST', 'PUT', 'DELETE'] }));
51
+ const limiter = rateLimit({ windowMs: 10 * 60 * 1000, max: 100 });
52
+ app.use(limiter);
53
+
54
+ app.use(express.json());
55
+ app.use(morgan('combined', { stream: { write: (message) => logger.info(message.trim()) } }));
56
+ <%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
57
+ app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpecs));
58
+ <%_ } -%>
59
+ <%_ if (viewEngine === 'EJS' || viewEngine === 'Pug') { -%>
60
+ // View Engine Setup
61
+ import path from 'path';
62
+ app.set('views', path.join(__dirname, 'views'));
63
+ app.set('view engine', '<%= viewEngine.toLowerCase() %>');
64
+ app.use(express.static(path.join(__dirname, '../public')));<%_ } %>
65
+ <%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
66
+ app.use('/api', apiRoutes);
67
+ <%_ } -%><% if (viewEngine && viewEngine !== 'None') { -%>
68
+ app.get('/', (req: Request, res: Response) => {
69
+ res.render('index', {
70
+ projectName: 'NodeJS Service',
71
+ architecture: 'MVC',
72
+ database: '<%= database %>',
73
+ communication: '<%= communication %>'
74
+ });
75
+ });
76
+ <% } -%>
77
+ app.use('/health', healthRoutes);
78
+
79
+ // Start Server Logic
80
+ const startServer = async () => {
81
+ <%_ if (communication === 'GraphQL') { -%>
82
+ // GraphQL Setup
83
+ const apolloServer = new ApolloServer<MyContext>({
84
+ typeDefs,
85
+ resolvers,
86
+ plugins: [ApolloServerPluginLandingPageLocalDefault({ embed: true })],
87
+ formatError: (formattedError, error) => {
88
+ const originalError = unwrapResolverError(error);
89
+ if (originalError instanceof ApiError) {
90
+ return {
91
+ ...formattedError,
92
+ message: originalError.message,
93
+ extensions: {
94
+ ...formattedError.extensions,
95
+ code: originalError.statusCode.toString(),
96
+ }
97
+ };
98
+ }
99
+
100
+ logger.error(`GraphQL Error: ${formattedError.message}`);
101
+ if (originalError instanceof Error && originalError.stack && process.env.NODE_ENV === 'development') {
102
+ logger.error(originalError.stack);
103
+ }
104
+ return formattedError;
105
+ },
106
+ });
107
+ await apolloServer.start();
108
+ app.use('/graphql', expressMiddleware(apolloServer, { context: gqlContext }));
109
+ <%_ } -%>
110
+ app.use(errorMiddleware);
111
+ const server = app.listen(port, () => {
112
+ logger.info(`Server running on port ${port}`);
113
+ <%_ if (communication === 'Kafka') { -%>
114
+ kafkaService.connect()
115
+ .then(async () => {
116
+ logger.info('Kafka connected');
117
+ })
118
+ .catch(err => {
119
+ logger.error('Failed to connect to Kafka after retries:', (err as Error).message);
120
+ });
121
+ <%_ } -%>
122
+ });
123
+
124
+ setupGracefulShutdown(server<% if(communication === 'Kafka') { %>, kafkaService<% } %>);
125
+ };
126
+
127
+ <%_ if (database !== 'None') { -%>
128
+ // Database Sync
129
+ <%_ if (database !== 'None') { -%>
130
+ import <% if (database === 'MongoDB') { %>connectDB<% } else { %>sequelize<% } %> from '@/config/database';
131
+ <%_ } -%>
132
+ const syncDatabase = async () => {
133
+ let retries = 30;
134
+ while (retries) {
135
+ try {
136
+ <%_ if (database === 'MongoDB') { -%>
137
+ await connectDB();
138
+ <%_ } else { -%>
139
+ await sequelize.sync();
140
+ <%_ } -%>
141
+ logger.info('Database synced');
142
+ // Start Server after DB is ready
143
+ await startServer();
144
+ break;
145
+ } catch (error) {
146
+ logger.error('Error syncing database:', error);
147
+ retries -= 1;
148
+ logger.info(`Retries left: ${retries}`);
149
+ await new Promise(res => setTimeout(res, 5000));
150
+ }
151
+ }
152
+ };
153
+
154
+ syncDatabase();
155
+ <%_ } else { -%>
156
+ startServer();
154
157
  <%_ } -%>
@@ -1,40 +1,59 @@
1
- import request from 'supertest';
2
- import express, { Express } from 'express';
3
- import router from '@/routes/api';
4
-
5
- const mockGetUsers = jest.fn().mockImplementation((req, res) => res.status(200).json([{ id: '1', name: 'John Doe' }]));
6
- const mockCreateUser = jest.fn().mockImplementation((req, res) => res.status(201).json({ id: '1', name: 'Test' }));
7
-
8
- jest.mock('@/controllers/userController', () => {
9
- return {
10
- UserController: jest.fn().mockImplementation(() => ({
11
- getUsers: (...args: unknown[]) => mockGetUsers(...args),
12
- createUser: (...args: unknown[]) => mockCreateUser(...args)
13
- }))
14
- };
15
- });
16
-
17
- describe('ApiRoutes', () => {
18
- let app: Express;
19
-
20
- beforeEach(() => {
21
- app = express();
22
- app.use(express.json());
23
- app.use('/api', router);
24
- });
25
-
26
- it('GET /users should call controller.getUsers', async () => {
27
- await request(app)
28
- .get('/api/users');
29
-
30
- expect(mockGetUsers).toHaveBeenCalledTimes(1);
31
- });
32
-
33
- it('POST /api/users should call controller.createUser', async () => {
34
- await request(app)
35
- .post('/api/users')
36
- .send({ name: 'Test', email: 'test@example.com' });
37
-
38
- expect(mockCreateUser).toHaveBeenCalledTimes(1);
39
- });
40
- });
1
+ import request from 'supertest';
2
+ import express, { Express } from 'express';
3
+ import router from '@/routes/api';
4
+
5
+ const mockGetUsers = jest.fn().mockImplementation((req, res) => res.status(200).json([{ id: '1', name: 'John Doe' }]));
6
+ const mockCreateUser = jest.fn().mockImplementation((req, res) => res.status(201).json({ id: '1', name: 'Test' }));
7
+ const mockUpdateUser = jest.fn().mockImplementation((req, res) => res.status(200).json({ id: '1', name: 'Updated' }));
8
+ const mockDeleteUser = jest.fn().mockImplementation((req, res) => res.status(204).send());
9
+
10
+ jest.mock('@/controllers/userController', () => {
11
+ return {
12
+ UserController: jest.fn().mockImplementation(() => ({
13
+ getUsers: (...args: unknown[]) => mockGetUsers(...args),
14
+ createUser: (...args: unknown[]) => mockCreateUser(...args),
15
+ updateUser: (...args: unknown[]) => mockUpdateUser(...args),
16
+ deleteUser: (...args: unknown[]) => mockDeleteUser(...args)
17
+ }))
18
+ };
19
+ });
20
+
21
+ describe('ApiRoutes', () => {
22
+ let app: Express;
23
+
24
+ beforeEach(() => {
25
+ app = express();
26
+ app.use(express.json());
27
+ app.use('/api', router);
28
+ });
29
+
30
+ it('GET /api/users should call controller.getUsers', async () => {
31
+ await request(app)
32
+ .get('/api/users');
33
+
34
+ expect(mockGetUsers).toHaveBeenCalledTimes(1);
35
+ });
36
+
37
+ it('POST /api/users should call controller.createUser', async () => {
38
+ await request(app)
39
+ .post('/api/users')
40
+ .send({ name: 'Test', email: 'test@example.com' });
41
+
42
+ expect(mockCreateUser).toHaveBeenCalledTimes(1);
43
+ });
44
+
45
+ it('PATCH /api/users/:id should call controller.updateUser', async () => {
46
+ await request(app)
47
+ .patch('/api/users/1')
48
+ .send({ name: 'Updated' });
49
+
50
+ expect(mockUpdateUser).toHaveBeenCalledTimes(1);
51
+ });
52
+
53
+ it('DELETE /api/users/:id should call controller.deleteUser', async () => {
54
+ await request(app)
55
+ .delete('/api/users/1');
56
+
57
+ expect(mockDeleteUser).toHaveBeenCalledTimes(1);
58
+ });
59
+ });