nodejs-quickstart-structure 2.0.1 → 2.1.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 (165) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +64 -66
  3. package/bin/index.js +5 -2
  4. package/lib/generator.js +10 -4
  5. package/lib/modules/app-setup.js +76 -6
  6. package/lib/modules/auth-setup.js +143 -0
  7. package/lib/modules/caching-setup.js +8 -1
  8. package/lib/modules/config-files.js +6 -0
  9. package/lib/modules/database-setup.js +2 -1
  10. package/lib/modules/project-setup.js +1 -0
  11. package/lib/prompts.js +39 -0
  12. package/package.json +5 -4
  13. package/templates/clean-architecture/js/src/domain/models/User.js +3 -1
  14. package/templates/clean-architecture/js/src/index.js.ejs +2 -0
  15. package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +12 -3
  16. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +25 -2
  17. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +38 -1
  18. package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +3 -0
  19. package/templates/clean-architecture/js/src/infrastructure/webserver/server.spec.js.ejs +51 -0
  20. package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.spec.js.ejs +14 -0
  21. package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +41 -4
  22. package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +70 -5
  23. package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -6
  24. package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +55 -22
  25. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +10 -5
  26. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +32 -10
  27. package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +1 -1
  28. package/templates/clean-architecture/js/src/interfaces/routes/api.js.ejs +15 -0
  29. package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +4 -0
  30. package/templates/clean-architecture/js/src/usecases/CreateUser.js.ejs +34 -0
  31. package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +12 -3
  32. package/templates/clean-architecture/js/src/usecases/DeleteUser.js.ejs +27 -0
  33. package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +9 -1
  34. package/templates/clean-architecture/js/src/usecases/GetAllUsers.js.ejs +36 -0
  35. package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +23 -1
  36. package/templates/clean-architecture/js/src/usecases/GetUserById.js.ejs +36 -0
  37. package/templates/clean-architecture/js/src/usecases/GetUserById.spec.js.ejs +48 -0
  38. package/templates/clean-architecture/js/src/usecases/UpdateUser.js.ejs +28 -0
  39. package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +9 -1
  40. package/templates/clean-architecture/js/src/utils/errorMessages.js +1 -0
  41. package/templates/clean-architecture/js/src/utils/httpCodes.js +2 -0
  42. package/templates/clean-architecture/ts/src/config/env.ts.ejs +12 -3
  43. package/templates/clean-architecture/ts/src/domain/user.ts +3 -1
  44. package/templates/clean-architecture/ts/src/index.ts.ejs +4 -0
  45. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +71 -10
  46. package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +32 -3
  47. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +43 -9
  48. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +57 -15
  49. package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +57 -24
  50. package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +14 -8
  51. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +33 -10
  52. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +15 -5
  53. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +1 -1
  54. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +9 -1
  55. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts.ejs +16 -0
  56. package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +12 -3
  57. package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +35 -0
  58. package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +10 -1
  59. package/templates/clean-architecture/ts/src/usecases/deleteUser.ts.ejs +24 -0
  60. package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +9 -1
  61. package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts.ejs +21 -0
  62. package/templates/clean-architecture/ts/src/usecases/getUserById.spec.ts.ejs +55 -0
  63. package/templates/clean-architecture/ts/src/usecases/getUserById.ts.ejs +23 -0
  64. package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +10 -1
  65. package/templates/clean-architecture/ts/src/usecases/updateUser.ts.ejs +25 -0
  66. package/templates/clean-architecture/ts/src/utils/errorMessages.ts +1 -0
  67. package/templates/clean-architecture/ts/src/utils/httpCodes.ts +1 -0
  68. package/templates/common/.cursorrules.ejs +9 -0
  69. package/templates/common/.env.example.ejs +17 -10
  70. package/templates/common/README.md.ejs +63 -18
  71. package/templates/common/auth/js/controllers/authController.js.ejs +170 -0
  72. package/templates/common/auth/js/controllers/authController.spec.js.ejs +148 -0
  73. package/templates/common/auth/js/middleware/authMiddleware.js.ejs +58 -0
  74. package/templates/common/auth/js/middleware/authMiddleware.spec.js.ejs +108 -0
  75. package/templates/common/auth/js/routes/authRoutes.js.ejs +16 -0
  76. package/templates/common/auth/js/services/jwtService.js.ejs +54 -0
  77. package/templates/common/auth/js/services/jwtService.spec.js.ejs +84 -0
  78. package/templates/common/auth/ts/controllers/authController.spec.ts.ejs +161 -0
  79. package/templates/common/auth/ts/controllers/authController.ts.ejs +167 -0
  80. package/templates/common/auth/ts/middleware/authMiddleware.spec.ts.ejs +128 -0
  81. package/templates/common/auth/ts/middleware/authMiddleware.ts.ejs +59 -0
  82. package/templates/common/auth/ts/routes/authRoutes.ts.ejs +20 -0
  83. package/templates/common/auth/ts/services/jwtService.spec.ts.ejs +89 -0
  84. package/templates/common/auth/ts/services/jwtService.ts.ejs +60 -0
  85. package/templates/common/babel.config.js.ejs +5 -0
  86. package/templates/common/caching/clean/js/CreateUser.js.ejs +14 -5
  87. package/templates/common/caching/clean/js/DeleteUser.js.ejs +2 -1
  88. package/templates/common/caching/clean/js/GetUserById.js.ejs +39 -0
  89. package/templates/common/caching/clean/js/UpdateUser.js.ejs +2 -1
  90. package/templates/common/caching/clean/ts/createUser.ts.ejs +14 -6
  91. package/templates/common/caching/clean/ts/deleteUser.ts.ejs +2 -1
  92. package/templates/common/caching/clean/ts/getUserById.ts.ejs +32 -0
  93. package/templates/common/caching/clean/ts/updateUser.ts.ejs +2 -1
  94. package/templates/common/caching/js/memoryCache.spec.js.ejs +2 -0
  95. package/templates/common/caching/js/redisClient.spec.js.ejs +2 -0
  96. package/templates/common/caching/ts/memoryCache.spec.ts.ejs +2 -0
  97. package/templates/common/caching/ts/redisClient.spec.ts.ejs +2 -0
  98. package/templates/common/database/js/models/User.js.ejs +14 -1
  99. package/templates/common/database/js/models/User.js.mongoose.ejs +7 -0
  100. package/templates/common/database/js/models/User.spec.js.ejs +12 -0
  101. package/templates/common/database/js/mongoose.spec.js.ejs +2 -0
  102. package/templates/common/database/ts/models/User.spec.ts.ejs +10 -0
  103. package/templates/common/database/ts/models/User.ts.ejs +17 -0
  104. package/templates/common/database/ts/models/User.ts.mongoose.ejs +8 -0
  105. package/templates/common/database/ts/mongoose.spec.ts.ejs +2 -0
  106. package/templates/common/docker-compose.yml.ejs +12 -0
  107. package/templates/common/ecosystem.config.js.ejs +9 -3
  108. package/templates/common/eslint.config.mjs.ejs +3 -0
  109. package/templates/common/jest.config.js.ejs +13 -9
  110. package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +1 -1
  111. package/templates/common/kafka/js/services/kafkaService.js.ejs +1 -1
  112. package/templates/common/migrations/init.js.ejs +5 -4
  113. package/templates/common/package.json.ejs +11 -2
  114. package/templates/common/prompts/project-context.md.ejs +8 -1
  115. package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +149 -107
  116. package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +88 -47
  117. package/templates/common/swagger.yml.ejs +148 -0
  118. package/templates/common/tsconfig.eslint.json +15 -0
  119. package/templates/common/tsconfig.json +3 -1
  120. package/templates/common/views/ejs/index.ejs +264 -30
  121. package/templates/common/views/ejs/login.ejs.ejs +244 -0
  122. package/templates/common/views/ejs/signup.ejs.ejs +282 -0
  123. package/templates/common/views/pug/index.pug +269 -38
  124. package/templates/common/views/pug/login.pug.ejs +195 -0
  125. package/templates/common/views/pug/signup.pug.ejs +241 -0
  126. package/templates/db/mysql/V1__Initial_Setup.sql.ejs +6 -0
  127. package/templates/db/postgres/V1__Initial_Setup.sql.ejs +6 -0
  128. package/templates/mvc/js/src/config/env.js.ejs +12 -3
  129. package/templates/mvc/js/src/controllers/userController.js.ejs +29 -5
  130. package/templates/mvc/js/src/controllers/userController.spec.js.ejs +27 -12
  131. package/templates/mvc/js/src/graphql/context.js.ejs +14 -3
  132. package/templates/mvc/js/src/graphql/context.spec.js.ejs +36 -21
  133. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +10 -5
  134. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +32 -10
  135. package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +1 -1
  136. package/templates/mvc/js/src/index.js.ejs +16 -3
  137. package/templates/mvc/js/src/routes/api.js.ejs +14 -0
  138. package/templates/mvc/js/src/routes/api.spec.js.ejs +3 -0
  139. package/templates/mvc/js/src/utils/errorMessages.js +1 -0
  140. package/templates/mvc/js/src/utils/httpCodes.js +1 -0
  141. package/templates/mvc/ts/src/config/env.ts.ejs +12 -3
  142. package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +95 -7
  143. package/templates/mvc/ts/src/controllers/userController.ts.ejs +68 -11
  144. package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +36 -23
  145. package/templates/mvc/ts/src/graphql/context.ts.ejs +15 -6
  146. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +32 -10
  147. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +15 -5
  148. package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +1 -1
  149. package/templates/mvc/ts/src/index.ts.ejs +15 -3
  150. package/templates/mvc/ts/src/routes/api.spec.ts.ejs +6 -0
  151. package/templates/mvc/ts/src/routes/api.ts.ejs +15 -0
  152. package/templates/mvc/ts/src/utils/errorMessages.ts +1 -0
  153. package/templates/mvc/ts/src/utils/httpCodes.ts +1 -0
  154. package/templates/clean-architecture/js/src/interfaces/routes/api.js +0 -12
  155. package/templates/clean-architecture/js/src/usecases/CreateUser.js +0 -14
  156. package/templates/clean-architecture/js/src/usecases/DeleteUser.js +0 -11
  157. package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +0 -12
  158. package/templates/clean-architecture/js/src/usecases/UpdateUser.js +0 -11
  159. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +0 -13
  160. package/templates/clean-architecture/ts/src/usecases/createUser.ts +0 -13
  161. package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +0 -9
  162. package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +0 -10
  163. package/templates/clean-architecture/ts/src/usecases/updateUser.ts +0 -9
  164. package/templates/mvc/js/src/routes/api.js +0 -10
  165. package/templates/mvc/ts/src/routes/api.ts +0 -12
@@ -0,0 +1,241 @@
1
+ doctype html
2
+ html(lang="en")
3
+ head
4
+ meta(charset="UTF-8")
5
+ meta(name="viewport" content="width=device-width, initial-scale=1.0")
6
+ title Sign Up - <%= projectName %>
7
+ link(href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap" rel="stylesheet")
8
+ style.
9
+ :root {
10
+ --primary: #22c55e;
11
+ --primary-glow: rgba(34, 197, 94, 0.4);
12
+ --bg-dark: #0f172a;
13
+ --card-bg: rgba(255, 255, 255, 0.03);
14
+ --card-border: rgba(255, 255, 255, 0.08);
15
+ --text-main: #f8fafc;
16
+ --text-muted: #94a3b8;
17
+ }
18
+
19
+ * {
20
+ margin: 0;
21
+ padding: 0;
22
+ box-sizing: border-box;
23
+ font-family: 'Poppins', sans-serif;
24
+ }
25
+
26
+ body {
27
+ background-color: var(--bg-dark);
28
+ color: var(--text-main);
29
+ min-height: 100vh;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ overflow-x: hidden;
34
+ position: relative;
35
+ }
36
+
37
+ .background-blobs {
38
+ position: absolute;
39
+ width: 100%;
40
+ height: 100%;
41
+ z-index: -1;
42
+ filter: blur(80px);
43
+ }
44
+
45
+ .blob {
46
+ position: absolute;
47
+ width: 500px;
48
+ height: 500px;
49
+ border-radius: 50%;
50
+ opacity: 0.3;
51
+ animation: float 25s infinite alternate;
52
+ }
53
+
54
+ .blob-1 {
55
+ background: radial-gradient(circle, #22c55e 0%, transparent 70%);
56
+ top: -200px;
57
+ right: -100px;
58
+ }
59
+
60
+ .blob-2 {
61
+ background: radial-gradient(circle, #3b82f6 0%, transparent 70%);
62
+ bottom: -200px;
63
+ left: -100px;
64
+ animation-delay: -7s;
65
+ }
66
+
67
+ @keyframes float {
68
+ 0% { transform: translate(0, 0) scale(1); }
69
+ 100% { transform: translate(-150px, 100px) scale(1.3); }
70
+ }
71
+
72
+ .auth-card {
73
+ background: var(--card-bg);
74
+ backdrop-filter: blur(15px);
75
+ -webkit-backdrop-filter: blur(15px);
76
+ border: 1px solid var(--card-border);
77
+ border-radius: 28px;
78
+ padding: 48px;
79
+ width: 100%;
80
+ max-width: 480px;
81
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.6);
82
+ z-index: 1;
83
+ margin: 40px 20px;
84
+ animation: fadeIn 0.8s ease-out;
85
+ }
86
+
87
+ @keyframes fadeIn {
88
+ from { opacity: 0; transform: translateY(15px); }
89
+ to { opacity: 1; transform: translateY(0); }
90
+ }
91
+
92
+ .auth-card h2 {
93
+ font-size: 32px;
94
+ font-weight: 600;
95
+ margin-bottom: 8px;
96
+ text-align: center;
97
+ background: linear-gradient(to right, #fff, #94a3b8);
98
+ -webkit-background-clip: text;
99
+ -webkit-text-fill-color: transparent;
100
+ }
101
+
102
+ .subtitle {
103
+ text-align: center;
104
+ color: var(--text-muted);
105
+ font-size: 14px;
106
+ margin-bottom: 40px;
107
+ }
108
+
109
+ .form-grid {
110
+ display: grid;
111
+ gap: 20px;
112
+ }
113
+
114
+ .form-group label {
115
+ display: block;
116
+ margin-bottom: 8px;
117
+ font-size: 13px;
118
+ font-weight: 500;
119
+ color: var(--text-muted);
120
+ }
121
+
122
+ .form-group input {
123
+ width: 100%;
124
+ background: rgba(255, 255, 255, 0.04);
125
+ border: 1px solid var(--card-border);
126
+ border-radius: 14px;
127
+ padding: 13px 18px;
128
+ color: #fff;
129
+ font-size: 15px;
130
+ transition: all 0.3s;
131
+ outline: none;
132
+ }
133
+
134
+ .form-group input:focus {
135
+ background: rgba(255, 255, 255, 0.07);
136
+ border-color: var(--primary);
137
+ box-shadow: 0 0 0 4px var(--primary-glow);
138
+ }
139
+
140
+ .btn-primary {
141
+ width: 100%;
142
+ background: var(--primary);
143
+ color: white;
144
+ border: none;
145
+ border-radius: 14px;
146
+ padding: 15px;
147
+ font-size: 16px;
148
+ font-weight: 600;
149
+ cursor: pointer;
150
+ transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
151
+ margin-top: 15px;
152
+ }
153
+
154
+ .btn-primary:hover {
155
+ transform: scale(1.02);
156
+ filter: brightness(1.1);
157
+ box-shadow: 0 15px 25px -5px rgba(34, 197, 94, 0.4);
158
+ }
159
+
160
+ .footer {
161
+ margin-top: 40px;
162
+ text-align: center;
163
+ font-size: 14px;
164
+ color: var(--text-muted);
165
+ }
166
+
167
+ .footer a {
168
+ color: var(--primary);
169
+ text-decoration: none;
170
+ font-weight: 600;
171
+ }
172
+
173
+ .back-home {
174
+ display: block;
175
+ margin-top: 20px;
176
+ font-size: 12px;
177
+ opacity: 0.5;
178
+ }
179
+ body
180
+ div.background-blobs
181
+ div.blob.blob-1
182
+ div.blob.blob-2
183
+ div.auth-card
184
+ h2 Join Us
185
+ p.subtitle Create your account in seconds
186
+ form.form-grid(action="/api/users" method="POST")
187
+ div.form-group
188
+ label(for="name") Full Name
189
+ input(type="text" id="name" name="name" placeholder="John Doe" required)
190
+ div.form-group
191
+ label(for="email") Email Address
192
+ input(type="email" id="email" name="email" placeholder="john@example.com" required)
193
+ div.form-group
194
+ label(for="password") Choose Password
195
+ input(type="password" id="password" name="password" placeholder="••••••••" required)
196
+ button.btn-primary(type="submit") Create Account
197
+ div.footer
198
+ p Already have an account?
199
+ a(href="/login") Sign in
200
+ a.back-home(href="/") ← Back to home
201
+
202
+ <% if (communication === 'GraphQL') { %>
203
+ script.
204
+ document.querySelector('form').addEventListener('submit', async (e) => {
205
+ e.preventDefault();
206
+ const formData = new FormData(e.target);
207
+ const data = Object.fromEntries(formData.entries());
208
+
209
+ const query = `
210
+ mutation CreateUser($name: String!, $email: String!, $password: String!) {
211
+ createUser(name: $name, email: $email, password: $password) {
212
+ id
213
+ name
214
+ email
215
+ }
216
+ }
217
+ `;
218
+
219
+ try {
220
+ const response = await fetch('/graphql', {
221
+ method: 'POST',
222
+ headers: { 'Content-Type': 'application/json' },
223
+ body: JSON.stringify({
224
+ query,
225
+ variables: data
226
+ })
227
+ });
228
+
229
+ const result = await response.json();
230
+ if (result.errors) {
231
+ alert('Error: ' + result.errors[0].message);
232
+ } else {
233
+ alert('Account created successfully! Please login.');
234
+ window.location.href = '/login';
235
+ }
236
+ } catch (err) {
237
+ console.error(err);
238
+ alert('An error occurred during signup.');
239
+ }
240
+ });
241
+ <% } %>
@@ -2,9 +2,15 @@ CREATE TABLE users (
2
2
  id INT AUTO_INCREMENT PRIMARY KEY,
3
3
  name VARCHAR(255) NOT NULL,
4
4
  email VARCHAR(255) NOT NULL UNIQUE,
5
+ <% if (auth.includes('JWT')) { %>password VARCHAR(255) NOT NULL, <% } %>
5
6
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
6
7
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
7
8
  deleted_at TIMESTAMP NULL
8
9
  );
9
10
 
11
+ <% if (auth.includes('JWT')) { %>
12
+ INSERT INTO users (name, email, password) VALUES ('Admin User', 'admin@example.com', '$2a$10$X.fO9PeyF0Lq0lF8uV6G9u4Vb4e5T0rF8l/JzM6S7X9u4Vb4e5T0r'); -- password: password123
13
+ <% } else { %>
10
14
  INSERT INTO users (name, email) VALUES ('Admin User', 'admin@example.com');
15
+ <% } %>
16
+
@@ -2,9 +2,15 @@ CREATE TABLE users (
2
2
  id SERIAL PRIMARY KEY,
3
3
  name VARCHAR(255) NOT NULL,
4
4
  email VARCHAR(255) NOT NULL UNIQUE,
5
+ <% if (auth.includes('JWT')) { %>password VARCHAR(255) NOT NULL, <% } %>
5
6
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
6
7
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
7
8
  deleted_at TIMESTAMP NULL
8
9
  );
9
10
 
11
+ <% if (auth.includes('JWT')) { %>
12
+ INSERT INTO users (name, email, password) VALUES ('Admin User', 'admin@example.com', '$2a$10$X.fO9PeyF0Lq0lF8uV6G9u4Vb4e5T0rF8l/JzM6S7X9u4Vb4e5T0r'); -- password: password123
13
+ <% } else { %>
10
14
  INSERT INTO users (name, email) VALUES ('Admin User', 'admin@example.com');
15
+ <% } %>
16
+
@@ -29,6 +29,12 @@ const envSchema = z.object({
29
29
  REDIS_PORT: z.string().transform(Number),
30
30
  REDIS_PASSWORD: z.string().optional(),
31
31
  <%_ } -%>
32
+ <%_ if (auth.includes('JWT')) { -%>
33
+ JWT_SECRET: z.string(),
34
+ JWT_EXPIRES_IN: z.string().default('15m'),
35
+ JWT_REFRESH_SECRET: z.string().default('your-secret-refresh-key'),
36
+ JWT_REFRESH_EXPIRES_IN: z.string().default('7d'),
37
+ <%_ } -%>
32
38
  <%_ if (communication === 'Kafka') { -%>
33
39
  KAFKA_BROKER: z.string(),
34
40
  <%_ } -%>
@@ -37,10 +43,13 @@ const envSchema = z.object({
37
43
  const _env = envSchema.safeParse(process.env);
38
44
 
39
45
  if (!_env.success) {
40
- logger.error('❌ Invalid environment variables:', _env.error.format());
41
- process.exit(1);
46
+ if (process.env.NODE_ENV !== 'test') {
47
+ logger.error('❌ Invalid environment variables:', _env.error.format());
48
+ process.exit(1);
49
+ }
50
+ logger.warn('⚠️ Environment validation failed. Continuing in test mode.');
42
51
  }
43
52
 
44
- const env = _env.data;
53
+ const env = _env.success ? _env.data : process.env;
45
54
 
46
55
  module.exports = { env };
@@ -13,7 +13,7 @@ const cacheService = require('../config/memoryCache');
13
13
  const { sendMessage } = require('../services/kafkaService');
14
14
  const { KAFKA_ACTIONS } = require('../utils/kafkaEvents');
15
15
  <%_ } -%>
16
-
16
+ <% if (auth.includes('JWT')) { %>const bcrypt = require('bcryptjs');<% } %>
17
17
  <% if (communication === 'GraphQL') { -%>
18
18
  const getUsers = async () => {
19
19
  try {
@@ -41,8 +41,19 @@ const getUsers = async () => {
41
41
 
42
42
  const createUser = async (data) => {
43
43
  try {
44
- const { name, email } = data;
44
+ const { name, email<% if (auth.includes('JWT')) { %>, password<% } %> } = data;
45
+ <% if (auth.includes('JWT')) { %>
46
+ let user;
47
+ if (password) {
48
+ const hashedPassword = await bcrypt.hash(password, 10);
49
+ user = await User.create({ name, email, password: hashedPassword });
50
+ } else {
51
+ user = await User.create({ name, email });
52
+ }
53
+ <% } else { %>
45
54
  const user = await User.create({ name, email });
55
+ <% } %>
56
+
46
57
  <%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
47
58
  await cacheService.del('users:all');
48
59
  <%_ } -%>
@@ -52,7 +63,9 @@ const createUser = async (data) => {
52
63
  payload: { id: user.id || user._id, email: user.email }
53
64
  }), (user.id || user._id).toString());
54
65
  <%_ } -%>
55
- return user;
66
+ const userObj = user.toJSON ? user.toJSON() : { ...user };
67
+ delete userObj.password;
68
+ return userObj;
56
69
  } catch (error) {
57
70
  logger.error(`${ERROR_MESSAGES.CREATE_USER_ERROR}:`, error);
58
71
  throw error;
@@ -148,8 +161,17 @@ const getUsers = async (req, res, next) => {
148
161
 
149
162
  const createUser = async (req, res, next) => {
150
163
  try {
151
- const { name, email } = req.body || {};
164
+ const { name, email, password } = req.body || {};
165
+ <% if (auth.includes('JWT')) { %>
166
+ if (!password) {
167
+ return res.status(HTTP_STATUS.BAD_REQUEST).json({ error: 'Password is required' });
168
+ }
169
+ const hashedPassword = await bcrypt.hash(password, 10);
170
+ const user = await User.create({ name, email, password: hashedPassword });
171
+ <% } else { %>
152
172
  const user = await User.create({ name, email });
173
+ <% } %>
174
+
153
175
  <%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
154
176
  await cacheService.del('users:all');
155
177
  <%_ } -%>
@@ -159,7 +181,9 @@ const createUser = async (req, res, next) => {
159
181
  payload: { id: user.id || user._id, email: user.email }
160
182
  }), (user.id || user._id).toString());
161
183
  <%_ } -%>
162
- res.status(HTTP_STATUS.CREATED).json(user);
184
+ const userObj = user.toJSON ? user.toJSON() : { ...user };
185
+ delete userObj.password;
186
+ res.status(HTTP_STATUS.CREATED).json(userObj);
163
187
  } catch (error) {
164
188
  logger.error(`${ERROR_MESSAGES.CREATE_USER_ERROR}:`, error);
165
189
  next(error);
@@ -1,5 +1,9 @@
1
+ <% if (communication !== 'GraphQL') { -%>
1
2
  const HTTP_STATUS = require('@/utils/httpCodes');
3
+ <% } -%>
4
+ <% if (communication === 'GraphQL') { -%>
2
5
  const ERROR_MESSAGES = require('@/utils/errorMessages');
6
+ <% } -%>
3
7
  <% if (communication !== 'GraphQL') { -%>
4
8
  // Express-only imports would go here
5
9
  <% } -%>
@@ -40,6 +44,11 @@ jest.mock('@/config/memoryCache', () => ({
40
44
  }));
41
45
  <%_ } -%>
42
46
  jest.mock('@/utils/logger');
47
+ <%_ if (auth.includes('JWT')) { _%>
48
+ jest.mock('bcryptjs', () => ({
49
+ hash: jest.fn().mockResolvedValue('hashed_password')
50
+ }));
51
+ <%_ } _%>
43
52
  <%_ if (communication === 'Kafka') { -%>
44
53
  jest.mock('@/services/kafkaService', () => {
45
54
  const mockSendMessage = jest.fn().mockResolvedValue(undefined);
@@ -166,7 +175,7 @@ describe('UserController', () => {
166
175
  describe('createUser', () => {
167
176
  it('should successfully create a new user (Happy Path)', async () => {
168
177
  // Arrange
169
- const payload = { name: 'Alice', email: 'alice@example.com' };
178
+ const payload = { name: 'Alice', email: 'alice@example.com', password: 'password123' };
170
179
  <% if (communication === 'GraphQL') { -%>
171
180
  const dataArg = payload;
172
181
  <% } else { -%>
@@ -185,20 +194,26 @@ describe('UserController', () => {
185
194
  const result = await createUser(dataArg);
186
195
 
187
196
  // Assert
188
- <%_ if (database === 'None') { -%>
197
+ expect(result.password).toBeUndefined();
189
198
  expect(result.name).toBe(payload.name);
190
199
  expect(result.email).toBe(payload.email);
191
- <%_ } else { -%>
192
- expect(result).toEqual(expectedUser);
193
- expect(User.create).toHaveBeenCalledWith(payload);
194
- <%_ } -%>
200
+
201
+ expect(User.create).toHaveBeenCalledWith({
202
+ name: payload.name,
203
+ email: payload.email
204
+ <%_ if (auth.includes('JWT')) { _%>, password: 'hashed_password'<%_ } _%>
205
+ });
195
206
  <% } else { -%>
196
207
  await createUser(mockRequest, mockResponse, mockNext);
197
208
 
198
209
  // Assert
199
210
  expect(mockResponse.status).toHaveBeenCalledWith(HTTP_STATUS.CREATED);
200
- expect(mockResponse.json).toHaveBeenCalledWith(expectedUser);
201
- expect(User.create).toHaveBeenCalledWith(payload);
211
+ expect(mockResponse.json).toHaveBeenCalledWith(expect.not.objectContaining({ password: expect.anything() }));
212
+ expect(User.create).toHaveBeenCalledWith({
213
+ name: payload.name,
214
+ email: payload.email
215
+ <%_ if (auth.includes('JWT')) { _%>, password: 'hashed_password'<%_ } _%>
216
+ });
202
217
  <%_ } -%>
203
218
  <%_ if (caching === 'Redis' || caching === 'Memory Cache') { -%>
204
219
  expect(cacheService.del).toHaveBeenCalledWith('users:all');
@@ -211,7 +226,7 @@ describe('UserController', () => {
211
226
  it('should handle errors when creation fails (Error Handling)', async () => {
212
227
  // Arrange
213
228
  const error = new Error('Creation Error');
214
- const payload = { name: 'Bob', email: 'bob@example.com' };
229
+ const payload = { name: 'Bob', email: 'bob@example.com', password: 'password123' };
215
230
  <% if (communication === 'GraphQL') { -%>
216
231
  const dataArg = payload;
217
232
  <% } else { -%>
@@ -232,7 +247,7 @@ describe('UserController', () => {
232
247
  <%_ if (communication === 'Kafka') { -%>
233
248
  it('should successfully create a new user with _id for Kafka (Happy Path)', async () => {
234
249
  // Arrange
235
- const payload = { name: 'Bob', email: 'bob@example.com' };
250
+ const payload = { name: 'Bob', email: 'bob@example.com', password: 'password123' };
236
251
  <% if (communication === 'GraphQL') { -%>
237
252
  const dataArg = payload;
238
253
  <% } else { -%>
@@ -446,9 +461,9 @@ describe('UserController', () => {
446
461
  const error = new Error('Database Error');
447
462
  User.create.mockRejectedValue(error);
448
463
  <% if (communication === 'GraphQL') { -%>
449
- await expect(createUser({ name: 'Alice', email: 'alice@example.com' })).rejects.toThrow(error);
464
+ await expect(createUser({ name: 'Alice', email: 'alice@example.com'<% if (auth.includes('JWT')) { %>, password: 'password123'<% } %> })).rejects.toThrow(error);
450
465
  <% } else { -%>
451
- mockRequest.body = { name: 'Alice', email: 'alice@example.com' };
466
+ mockRequest.body = { name: 'Alice', email: 'alice@example.com', password: 'password123' };
452
467
  await createUser(mockRequest, mockResponse, mockNext);
453
468
  expect(mockNext).toHaveBeenCalledWith(error);
454
469
  <% } -%>
@@ -1,7 +1,18 @@
1
+ <% if (auth.includes('JWT')) { %>const JwtService = require('../services/jwtService');<% } %>
2
+
1
3
  const gqlContext = async ({ req }) => {
2
- // Setup authorization or context here
3
- const token = req.headers.authorization || '';
4
- return { token };
4
+ const context = {};
5
+ <% if (auth.includes('JWT')) { %>
6
+ const authHeader = req.headers.authorization || '';
7
+ if (authHeader.startsWith('Bearer ')) {
8
+ const token = authHeader.split(' ')[1];
9
+ const user = JwtService.verifyToken(token);
10
+ if (user) {
11
+ context.user = user;
12
+ }
13
+ }<% } %>
14
+
15
+ return context;
5
16
  };
6
17
 
7
18
  module.exports = { gqlContext };
@@ -1,29 +1,44 @@
1
+ <% if (auth.includes('JWT')) { %>const JwtService = require('@/services/jwtService');
2
+ jest.mock('@/services/jwtService');<% } %>
1
3
  const { gqlContext } = require('@/graphql/context');
2
- const { resolvers } = require('@/graphql/resolvers');
3
- const { typeDefs } = require('@/graphql/typeDefs');
4
+ const { resolvers } = require('@/graphql/index');
5
+ const { typeDefs } = require('@/graphql/typeDefs/index');
4
6
 
5
7
  describe('GraphQL Context', () => {
6
- it('should exercise GraphQL index entry points', () => {
7
- expect(resolvers).toBeDefined();
8
- expect(typeDefs).toBeDefined();
8
+ afterEach(() => {
9
+ jest.clearAllMocks();
9
10
  });
10
- it('should return context with token when authorization header is present', async () => {
11
- const mockRequest = {
12
- headers: {
13
- authorization: 'Bearer token123',
14
- },
15
- };
16
11
 
17
- const context = await gqlContext({ req: mockRequest });
18
- expect(context).toEqual({ token: 'Bearer token123' });
19
- });
12
+ it('should return context with user when authorization header is present and valid', async () => {
13
+ <% if (auth.includes('JWT')) { %>
14
+ const mockUser = { id: '1', email: 'test@test.com' };
15
+ JwtService.verifyToken.mockReturnValue(mockUser);
16
+ const mockRequest = {
17
+ headers: {
18
+ authorization: 'Bearer valid-token',
19
+ },
20
+ };
20
21
 
21
- it('should return context with empty token when authorization header is missing', async () => {
22
- const mockRequest = {
23
- headers: {},
24
- };
22
+ const context = await gqlContext({ req: mockRequest });
23
+ expect(context.user).toEqual(mockUser);
24
+ expect(JwtService.verifyToken).toHaveBeenCalledWith('valid-token');
25
+ <% } else { %>
26
+ const mockRequest = {
27
+ headers: {
28
+ authorization: 'Bearer token123',
29
+ },
30
+ };
31
+ const context = await gqlContext({ req: mockRequest });
32
+ expect(context).toEqual({});
33
+ <% } %>
34
+ });
35
+
36
+ it('should return empty context when authorization header is missing', async () => {
37
+ const mockRequest = {
38
+ headers: {},
39
+ };
25
40
 
26
- const context = await gqlContext({ req: mockRequest });
27
- expect(context).toEqual({ token: '' });
28
- });
41
+ const context = await gqlContext({ req: mockRequest });
42
+ expect(context).toEqual({});
43
+ });
29
44
  });
@@ -2,18 +2,23 @@ const userController = require('../../controllers/userController');
2
2
 
3
3
  const userResolvers = {
4
4
  Query: {
5
- getAllUsers: async () => {
5
+ getAllUsers: async (_, __, <% if (auth.includes('JWT')) { %>{ user }<% } else { %>_context<% } %>) => {
6
+ <% if (auth.includes('JWT')) { %>if (!user) throw new Error('Unauthorized');<% } %>
6
7
  return await userController.getUsers();
7
8
  }
8
9
  },
9
10
  Mutation: {
10
- createUser: async (_, { name, email }) => {
11
- return await userController.createUser({ name, email });
11
+ createUser: async (_, { name, email<% if (auth.some(a => a !=='None')) { %>, password<% } %> }) => {
12
+ // Create user is typically public for registration
13
+ const user = await userController.createUser({ name, email<% if (auth.some(a => a !=='None')) { %>, password<% } %> });
14
+ return user;
12
15
  },
13
- updateUser: async (_, { id, name, email }) => {
16
+ updateUser: async (_, { id, name, email }, <% if (auth.includes('JWT')) { %>{ user }<% } else { %>_context<% } %>) => {
17
+ <% if (auth.includes('JWT')) { %>if (!user) throw new Error('Unauthorized');<% } %>
14
18
  return await userController.updateUser(id, { name, email });
15
19
  },
16
- deleteUser: async (_, { id }) => {
20
+ deleteUser: async (_, { id }, <% if (auth.includes('JWT')) { %>{ user }<% } else { %>_context<% } %>) => {
21
+ <% if (auth.includes('JWT')) { %>if (!user) throw new Error('Unauthorized');<% } %>
17
22
  return await userController.deleteUser(id);
18
23
  }
19
24
  }<%_ if (database === 'MongoDB') { -%>,
@@ -11,40 +11,62 @@ jest.mock('@/controllers/userController', () => ({
11
11
  }));
12
12
 
13
13
  describe('User Resolvers', () => {
14
+ const mockContext = {
15
+ <% if (auth.includes('JWT')) { %>user: { id: 'admin', email: 'admin@test.com' }<% } %>
16
+ };
17
+
14
18
  afterEach(() => {
15
19
  jest.clearAllMocks();
16
20
  });
17
21
 
18
22
  describe('Query.getAllUsers', () => {
19
- it('should return all users', async () => {
20
- const result = await userResolvers.Query.getAllUsers();
23
+ it('should return all users when authorized', async () => {
24
+ const result = await userResolvers.Query.getAllUsers(null, null, mockContext);
21
25
  expect(result).toEqual([{ id: '1', name: 'John Doe', email: 'john@example.com' }]);
22
26
  expect(mockGetUsers).toHaveBeenCalledTimes(1);
23
27
  });
28
+
29
+ <% if (auth.includes('JWT')) { %>
30
+ it('should throw error when unauthorized', async () => {
31
+ await expect(userResolvers.Query.getAllUsers(null, null, {})).rejects.toThrow('Unauthorized');
32
+ });
33
+ <% } %>
24
34
  });
25
35
 
26
36
  describe('Mutation.createUser', () => {
27
- it('should create and return a new user', async () => {
28
- const result = await userResolvers.Mutation.createUser(null, { name: 'Jane', email: 'jane@example.com' });
37
+ it('should create and return a new user (Public)', async () => {
38
+ const payload = { name: 'Jane', email: 'jane@example.com'<% if (auth.some(a => a !=='None')) { %>, password: 'password123'<% } %> };
39
+ const result = await userResolvers.Mutation.createUser(null, payload);
29
40
  expect(result).toEqual({ id: '1', name: 'Jane', email: 'jane@example.com' });
30
- expect(mockCreateUser).toHaveBeenCalledWith({ name: 'Jane', email: 'jane@example.com' });
31
- expect(mockCreateUser).toHaveBeenCalledTimes(1);
41
+ expect(mockCreateUser).toHaveBeenCalledWith(payload);
32
42
  });
33
43
  });
34
44
 
35
45
  describe('Mutation.updateUser', () => {
36
- it('should update and return the user', async () => {
46
+ it('should update and return the user when authorized', async () => {
37
47
  const payload = { name: 'Updated' };
38
- const result = await userResolvers.Mutation.updateUser(null, { id: '1', ...payload });
48
+ const result = await userResolvers.Mutation.updateUser(null, { id: '1', ...payload }, mockContext);
39
49
  expect(result).toMatchObject(payload);
40
50
  });
51
+
52
+ <% if (auth.includes('JWT')) { %>
53
+ it('should throw error when unauthorized', async () => {
54
+ await expect(userResolvers.Mutation.updateUser(null, { id: '1', name: 'N' }, {})).rejects.toThrow('Unauthorized');
55
+ });
56
+ <% } %>
41
57
  });
42
58
 
43
59
  describe('Mutation.deleteUser', () => {
44
- it('should delete and return true', async () => {
45
- const result = await userResolvers.Mutation.deleteUser(null, { id: '1' });
60
+ it('should delete and return true when authorized', async () => {
61
+ const result = await userResolvers.Mutation.deleteUser(null, { id: '1' }, mockContext);
46
62
  expect(result).toBe(true);
47
63
  });
64
+
65
+ <% if (auth.includes('JWT')) { %>
66
+ it('should throw error when unauthorized', async () => {
67
+ await expect(userResolvers.Mutation.deleteUser(null, { id: '1' }, {})).rejects.toThrow('Unauthorized');
68
+ });
69
+ <% } %>
48
70
  });
49
71
  <%_ if (database === 'MongoDB') { -%>
50
72
  describe('User.id', () => {
@@ -10,7 +10,7 @@ const userTypes = `#graphql
10
10
  }
11
11
 
12
12
  type Mutation {
13
- createUser(name: String!, email: String!): User
13
+ createUser(name: String!, email: String!<%_ if (auth.some(a => a !=='None')) { _%>, password: String!<%_ } _%>): User
14
14
  updateUser(id: ID!, name: String, email: String): User
15
15
  deleteUser(id: ID!): Boolean
16
16
  }