@ifecodes/backend-template 1.1.8 → 1.4.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 (93) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +423 -365
  3. package/bin/cli.js +1276 -836
  4. package/bin/lib/microservice-config.js +155 -150
  5. package/bin/lib/prompts.js +277 -241
  6. package/bin/lib/readme-generator.js +364 -335
  7. package/bin/lib/service-setup.js +901 -679
  8. package/package.json +64 -55
  9. package/template/base/js/.env.example +5 -5
  10. package/template/base/js/.eslintrc.json +10 -13
  11. package/template/base/js/.husky/pre-commit +1 -7
  12. package/template/base/js/.prettierrc +7 -7
  13. package/template/base/js/eslint.config.js +33 -31
  14. package/template/base/js/package.json +29 -28
  15. package/template/base/js/src/app.js +20 -15
  16. package/template/base/js/src/config/env.js +32 -2
  17. package/template/base/js/src/config/index.js +2 -2
  18. package/template/base/js/src/docs/index.js +5 -0
  19. package/template/base/js/src/docs/route-registry.js +63 -0
  20. package/template/base/js/src/middlewares/error-handler.middleware.js +22 -0
  21. package/template/base/js/src/middlewares/index.js +9 -3
  22. package/template/base/js/src/middlewares/method-not-allowed.middleware.js +8 -2
  23. package/template/base/js/src/middlewares/not-found.middleware.js +4 -1
  24. package/template/base/js/src/middlewares/observability.middleware.js +24 -0
  25. package/template/base/js/src/middlewares/root.middleware.js +7 -5
  26. package/template/base/js/src/middlewares/validation.middleware.js +39 -0
  27. package/template/base/js/src/modules/index.js +8 -8
  28. package/template/base/js/src/modules/v1/health/health.controller.auth.js +29 -0
  29. package/template/base/js/src/modules/v1/health/health.controller.js +4 -4
  30. package/template/base/js/src/modules/v1/health/health.route.js +70 -5
  31. package/template/base/js/src/modules/v1/health/index.js +1 -1
  32. package/template/base/js/src/modules/v1/index.js +3 -3
  33. package/template/base/js/src/routes.js +13 -6
  34. package/template/base/js/src/server.js +18 -18
  35. package/template/base/js/src/utils/http-error.js +27 -6
  36. package/template/base/js/src/utils/index.js +28 -22
  37. package/template/base/js/src/utils/logger.js +57 -67
  38. package/template/base/ts/.eslintrc.json +13 -17
  39. package/template/base/ts/.prettierrc +7 -7
  40. package/template/base/ts/eslint.config.js +33 -33
  41. package/template/base/ts/package.json +41 -39
  42. package/template/base/ts/src/app.ts +20 -15
  43. package/template/base/ts/src/config/db.ts +4 -4
  44. package/template/base/ts/src/config/env.ts +40 -10
  45. package/template/base/ts/src/config/index.ts +2 -2
  46. package/template/base/ts/src/docs/index.ts +3 -0
  47. package/template/base/ts/src/docs/route-registry.ts +98 -0
  48. package/template/base/ts/src/middlewares/error-handler.middleware.ts +26 -0
  49. package/template/base/ts/src/middlewares/index.ts +6 -3
  50. package/template/base/ts/src/middlewares/method-not-allowed.middleware.ts +23 -16
  51. package/template/base/ts/src/middlewares/not-found.middleware.ts +10 -8
  52. package/template/base/ts/src/middlewares/observability.middleware.ts +25 -0
  53. package/template/base/ts/src/middlewares/root.middleware.ts +16 -14
  54. package/template/base/ts/src/middlewares/validation.middleware.ts +46 -0
  55. package/template/base/ts/src/modules/index.ts +8 -8
  56. package/template/base/ts/src/modules/v1/health/health.controller.auth.ts +26 -0
  57. package/template/base/ts/src/modules/v1/health/health.controller.ts +18 -18
  58. package/template/base/ts/src/modules/v1/health/health.route.ts +68 -9
  59. package/template/base/ts/src/modules/v1/health/index.ts +1 -1
  60. package/template/base/ts/src/modules/v1/index.ts +8 -8
  61. package/template/base/ts/src/routes.ts +23 -15
  62. package/template/base/ts/src/server.ts +19 -19
  63. package/template/base/ts/src/utils/http-error.ts +63 -45
  64. package/template/base/ts/src/utils/index.ts +14 -11
  65. package/template/base/ts/src/utils/logger.ts +58 -68
  66. package/template/base/ts/tsconfig.json +21 -22
  67. package/template/features/auth/argon2/inject.js +50 -50
  68. package/template/features/auth/base/health-openapi.ts +62 -0
  69. package/template/features/auth/base/inject.js +174 -172
  70. package/template/features/auth/bcrypt/inject.js +40 -40
  71. package/template/features/auth/models/user.model.js +24 -24
  72. package/template/features/auth/models/user.model.ts +1 -1
  73. package/template/features/auth/modules/auth.controller.js +21 -21
  74. package/template/features/auth/modules/auth.controller.ts +28 -20
  75. package/template/features/auth/modules/auth.routes.js +89 -10
  76. package/template/features/auth/modules/auth.routes.ts +86 -11
  77. package/template/features/auth/modules/auth.service.js +29 -29
  78. package/template/features/auth/modules/auth.service.ts +38 -38
  79. package/template/features/auth/modules/index.js +1 -1
  80. package/template/features/auth/utils/hash.ts +20 -20
  81. package/template/features/auth/utils/jwt.js +12 -12
  82. package/template/features/cors/inject.js +14 -13
  83. package/template/features/helmet/inject.js +7 -6
  84. package/template/features/morgan/inject.js +8 -7
  85. package/template/features/rate-limit/inject.js +7 -6
  86. package/template/gateway/js/app.js +42 -42
  87. package/template/gateway/js/inject.js +33 -31
  88. package/template/gateway/js/server.js +19 -19
  89. package/template/gateway/ts/app.ts +43 -43
  90. package/template/gateway/ts/inject.js +33 -33
  91. package/template/gateway/ts/server.ts +19 -19
  92. package/template/microservice/docker/docker-compose.yml +5 -5
  93. package/template/microservice/nodocker/pm2.config.js +3 -3
@@ -1,335 +1,364 @@
1
- import fs from "fs";
2
- import path from "path";
3
-
4
- export const generateReadme = (config, serviceName = null) => {
5
- const { projectType, mode, features = [], auth, sanitizedName } = config;
6
- const isMicroservice = projectType === "microservice";
7
-
8
- let readme = `# ${serviceName || sanitizedName}\n\n`;
9
-
10
- // Description
11
- if (isMicroservice && serviceName) {
12
- readme += `A microservice for ${sanitizedName}.\n\n`;
13
- } else if (isMicroservice) {
14
- readme += `A microservices-based backend application.\n\n`;
15
- } else {
16
- readme += `A monolithic backend API application.\n\n`;
17
- }
18
-
19
- // Architecture
20
- readme += `## Architecture\n\n`;
21
- if (isMicroservice) {
22
- readme += `- **Type**: Microservice\n`;
23
- readme += `- **Deployment**: ${mode === "docker" ? "Docker" : "PM2"}\n`;
24
- readme += `- **Gateway**: Port 4000 (main entry point)\n`;
25
- readme += `- **Services**:\n`;
26
- const servicesList = (config.allServices && config.allServices.length)
27
- ? config.allServices
28
- : ["gateway", "health-service", ...(auth ? ["auth-service"] : [])];
29
- servicesList.forEach((service, idx) => {
30
- const isGateway = service === "gateway";
31
- const port = isGateway
32
- ? 4000
33
- : 4001 + servicesList.filter((s) => s !== "gateway" && servicesList.indexOf(s) < idx).length;
34
- const pretty = service
35
- .split("-")
36
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
37
- .join(" ");
38
- readme += ` - ${pretty} (port ${port})\n`;
39
- });
40
- readme += `\n`;
41
- } else {
42
- readme += `- **Type**: Monolith API\n`;
43
- readme += `- **Port**: 4000 (default)\n\n`;
44
- }
45
-
46
- // Tech Stack
47
- readme += `## Tech Stack\n\n`;
48
- readme += `- **Runtime**: Node.js\n`;
49
- readme += `- **Language**: TypeScript\n`;
50
- readme += `- **Framework**: Express.js\n`;
51
- if (auth) readme += `- **Database**: MongoDB (Mongoose)\n`;
52
-
53
- if (features.length > 0 || auth) {
54
- readme += `- **Features**:\n`;
55
- if (features.includes("cors")) readme += ` - CORS\n`;
56
- if (features.includes("helmet")) readme += ` - Helmet (Security headers)\n`;
57
- if (features.includes("rate-limit")) readme += ` - Rate Limiting\n`;
58
- if (features.includes("morgan")) readme += ` - Morgan (HTTP logging)\n`;
59
- if (auth) readme += ` - Authentication (JWT)\n`;
60
- }
61
- readme += `\n`;
62
-
63
- // Getting Started
64
- readme += `## Getting Started\n\n`;
65
- readme += `### Prerequisites\n\n`;
66
- readme += `- Node.js (v18 or higher)\n`;
67
- readme += `- npm or yarn\n`;
68
- if (auth) readme += `- MongoDB\n`;
69
- if (isMicroservice && mode === "docker") readme += `- Docker & Docker Compose\n`;
70
- if (isMicroservice && mode === "nodocker") readme += `- PM2 (\`npm install -g pm2\`)\n`;
71
- readme += `\n`;
72
-
73
- // Installation
74
- readme += `### Installation\n\n`;
75
- readme += `1. Clone the repository\n`;
76
- readme += `\`\`\`bash\n`;
77
- readme += `cd ${sanitizedName}\n`;
78
- readme += `\`\`\`\n\n`;
79
-
80
- if (isMicroservice) {
81
- readme += `2. Install dependencies for all services\n`;
82
- readme += `\`\`\`bash\n`;
83
- readme += `# Install root dependencies (Husky)\n`;
84
- readme += `npm install\n\n`;
85
- readme += `# Install dependencies for each service\n`;
86
- readme += `cd services/gateway && npm install && cd ../..\n`;
87
- readme += `cd services/health-service && npm install && cd ../..\n`;
88
- if (auth) readme += `cd services/auth-service && npm install && cd ../..\n`;
89
- readme += `\`\`\`\n\n`;
90
- } else {
91
- readme += `2. Install dependencies\n`;
92
- readme += `\`\`\`bash\n`;
93
- readme += `npm install\n`;
94
- readme += `\`\`\`\n\n`;
95
- }
96
-
97
- // Environment Variables
98
- readme += `3. Set up environment variables\n`;
99
- if (isMicroservice) {
100
- readme += `\`\`\`bash\n`;
101
- readme += `# Environment variables are configured in docker-compose.yml or pm2.config.js\n`;
102
- readme += `# No .env files needed for individual services\n`;
103
- readme += `\`\`\`\n\n`;
104
- } else {
105
- readme += `\`\`\`bash\n`;
106
- readme += `cp .env.example .env\n`;
107
- readme += `\`\`\`\n\n`;
108
- }
109
-
110
- if (auth) {
111
- readme += `4. Configure your MongoDB connection and JWT secret in the \`.env\` file${isMicroservice ? "s" : ""}\n\n`;
112
- }
113
-
114
- // Running the Application
115
- readme += `## Running the Application\n\n`;
116
-
117
- if (isMicroservice && mode === "docker") {
118
- readme += `### With Docker\n\n`;
119
- readme += `\`\`\`bash\n`;
120
- readme += `# Start all services\n`;
121
- readme += `npm run dev\n\n`;
122
- readme += `# Start in detached mode\n`;
123
- readme += `npm run dev -d\n\n`;
124
- readme += `# Stop all services\n`;
125
- readme += `npm stop\n`;
126
- readme += `\`\`\`\n\n`;
127
- } else if (isMicroservice && mode === "nodocker") {
128
- readme += `### With PM2\n\n`;
129
- readme += `\`\`\`bash\n`;
130
- readme += `# Start all services\n`;
131
- readme += `pm2 start pm2.config.js\n\n`;
132
- readme += `# View logs\n`;
133
- readme += `pm2 logs\n\n`;
134
- readme += `# Stop all services\n`;
135
- readme += `pm2 stop all\n\n`;
136
- readme += `# Delete all services\n`;
137
- readme += `pm2 delete all\n`;
138
- readme += `\`\`\`\n\n`;
139
- } else {
140
- readme += `### Development\n\n`;
141
- readme += `\`\`\`bash\n`;
142
- readme += `npm run dev\n`;
143
- readme += `\`\`\`\n\n`;
144
- readme += `### Production\n\n`;
145
- readme += `\`\`\`bash\n`;
146
- readme += `npm run build\n`;
147
- readme += `npm start\n`;
148
- readme += `\`\`\`\n\n`;
149
- }
150
-
151
- // API Endpoints
152
- readme += `## API Endpoints\n\n`;
153
-
154
- if (isMicroservice) {
155
- readme += `All requests go through the API Gateway at \`http://localhost:4000\`\n\n`;
156
-
157
- readme += `### Gateway Endpoints\n`;
158
- readme += `- **GET** \`/\` - API information and available endpoints\n`;
159
- readme += `- **GET** \`/health\` - Gateway health check\n`;
160
-
161
- readme += `### Health Service (Proxied through Gateway)\n`;
162
- readme += `- **GET** \`/api/v1/health\` - Service health check with system metrics\n`;
163
- readme += ` - Returns: status, uptime, timestamp, memory usage\n\n`;
164
-
165
- if (auth) {
166
- readme += `### Auth Service (Proxied through Gateway)\n`;
167
- readme += `- **POST** \`/api/v1/auth/register\` - Register a new user\n`;
168
- readme += `- **POST** \`/api/v1/auth/login\` - Login user\n\n`;
169
- }
170
-
171
- readme += `### Direct Service Access (Development Only)\n`;
172
- const directServices = (config.allServices && config.allServices.length)
173
- ? config.allServices
174
- : ["gateway", "health-service", ...(auth ? ["auth-service"] : [])];
175
- directServices.forEach((service) => {
176
- const isGateway = service === "gateway";
177
- const port = isGateway
178
- ? 4000
179
- : 4001 + directServices.filter((s) => s !== "gateway" && directServices.indexOf(s) < directServices.indexOf(service)).length;
180
- const basePath = isGateway ? `` : `/api/v1`;
181
- readme += `- **${service}**: \`http://localhost:${port}${basePath}\`\n`;
182
- });
183
- readme += `\n`;
184
-
185
- readme += "### Example Requests\n";
186
- readme += "```bash\n";
187
- readme += "# Gateway info\n";
188
- readme += "curl http://localhost:4000/\n\n";
189
- readme += "# Gateway health\n";
190
- readme += "curl http://localhost:4000/health\n\n";
191
-
192
- // Direct access examples for non-gateway services
193
- const exampleServices = (config.allServices && config.allServices.length)
194
- ? config.allServices
195
- : ["gateway", "health-service", ...(auth ? ["auth-service"] : [])];
196
- exampleServices.forEach((service) => {
197
- if (service === "gateway") return; // gateway already covered
198
- const port = service === "gateway" ? 4000 : 4001 + exampleServices.filter((s) => s !== "gateway" && exampleServices.indexOf(s) < exampleServices.indexOf(service)).length;
199
- readme += `# ${service} (direct access)\n`;
200
- readme += `curl http://localhost:${port}/api/v1/health\n\n`;
201
- });
202
-
203
- if (auth && exampleServices.includes("auth-service")) {
204
- const authPort = 4001 + exampleServices.filter((s) => s !== "gateway" && exampleServices.indexOf(s) < exampleServices.indexOf("auth-service")).length;
205
- readme += "# Auth requests (through gateway)\n";
206
- readme += `curl -X POST http://localhost:4000/api/v1/auth/register \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
207
- readme += `curl -X POST http://localhost:4000/api/v1/auth/login \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
208
- readme += `# Auth requests (direct access)\n`;
209
- readme += `curl -X POST http://localhost:${authPort}/api/v1/auth/register \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
210
- readme += `curl -X POST http://localhost:${authPort}/api/v1/auth/login \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
211
- }
212
-
213
- readme += "```\n\n";
214
-
215
- } else {
216
- readme += `Base URL: \`http://localhost:4000\`\n\n`;
217
- readme += `- **GET** \`/\` - Root endpoint (API info)\n`;
218
- readme += `- **GET** \`/api/v1/health\` - Health check\n\n`;
219
-
220
- if (auth) {
221
- readme += `### Authentication\n`;
222
- readme += `- **POST** \`/api/v1/auth/register\` - Register a new user\n`;
223
- readme += `- **POST** \`/api/v1/auth/login\` - Login user\n\n`;
224
- }
225
-
226
- // Example requests for monolith
227
- readme += "### Example Requests\n";
228
- readme += "```bash\n";
229
- readme += "# Root info\n";
230
- readme += "curl http://localhost:4000/\n\n";
231
- readme += "# Health check\n";
232
- readme += "curl http://localhost:4000/api/v1/health\n\n";
233
- if (auth) {
234
- readme += "# Register user\n";
235
- readme += `curl -X POST http://localhost:4000/api/v1/auth/register \\\n`;
236
- readme += ` -H \"Content-Type: application/json\" \\\n`;
237
- readme += ` -d '{"username":"testuser","password":"password123"}'\n\n`;
238
- readme += "# Login user\n";
239
- readme += `curl -X POST http://localhost:4000/api/v1/auth/login \\\n`;
240
- readme += ` -H \"Content-Type: application/json\" \\\n`;
241
- readme += ` -d '{"username":"testuser","password":"password123"}'\n\n`;
242
- }
243
- readme += "```\n\n";
244
- }
245
-
246
- // Project Structure
247
- readme += `## Project Structure\n\n`;
248
- readme += `\`\`\`\n`;
249
-
250
- if (isMicroservice) {
251
- readme += `${sanitizedName}/\n`;
252
- readme += `├── shared/ # Shared utilities across services\n`;
253
- readme += `│ ├── config/ # Database, environment configs\n`;
254
- readme += `│ └── utils/ # Logger, error handlers\n`;
255
- readme += `├── services/\n`;
256
- const projectServices = (config.allServices && config.allServices.length)
257
- ? config.allServices
258
- : ["gateway", "health-service", ...(auth ? ["auth-service"] : [])];
259
- projectServices.forEach((service) => {
260
- readme += `│ ├── ${service}/\n`;
261
- });
262
- readme += `├── ${mode === "docker" ? "docker-compose.yml" : "pm2.config.js"}\n`;
263
- readme += `├── .husky/ # Git hooks\n`;
264
- readme += `└── package.json # Root package.json\n`;
265
- } else {
266
- readme += `${sanitizedName}/\n`;
267
- readme += `├── src/\n`;
268
- readme += `│ ├── config/ # Configuration files\n`;
269
- readme += `│ ├── middlewares/ # Custom middlewares\n`;
270
- readme += `│ ├── modules/ # Feature modules\n`;
271
- readme += `│ │ └── v1/ # API version 1\n`;
272
- if (auth) readme += `│ │ ├── auth/ # Auth module\n`;
273
- readme += `│ │ └── health/ # Health check\n`;
274
- if (auth) readme += `│ ├── models/ # Database models\n`;
275
- readme += `│ ├── utils/ # Utility functions\n`;
276
- readme += `│ ├── app.ts # Express app setup\n`;
277
- readme += `│ ├── routes.ts # Route definitions\n`;
278
- readme += `│ └── server.ts # Server entry point\n`;
279
- readme += `├── .husky/ # Git hooks\n`;
280
- readme += `├── package.json\n`;
281
- readme += `└── tsconfig.json\n`;
282
- }
283
- readme += `\`\`\`\n\n`;
284
-
285
- // Scripts
286
- readme += `## Available Scripts\n\n`;
287
- if (isMicroservice) {
288
- if (mode === "docker") {
289
- readme += `- \`npm run dev\` - Start all services\n`;
290
- readme += `- \`npm stop\` - Stop all services\n`;
291
- readme += `- \`docker-compose logs -f [service-name]\` - View service logs\n`;
292
- } else {
293
- readme += `- \`pm2 start pm2.config.js\` - Start all services\n`;
294
- readme += `- \`pm2 logs\` - View all service logs\n`;
295
- readme += `- \`pm2 monit\` - Monitor services\n`;
296
- readme += `- \`pm2 stop all\` - Stop all services\n`;
297
- }
298
- } else {
299
- readme += `- \`npm run dev\` - Start development server with hot reload\n`;
300
- readme += `- \`npm run build\` - Build for production\n`;
301
- readme += `- \`npm start\` - Start production server\n`;
302
- readme += `- \`npm run lint\` - Run ESLint\n`;
303
- readme += `- \`npm run format\` - Run Prettier\n`;
304
- }
305
- readme += `\n`;
306
-
307
- // Environment Variables
308
- readme += `## Environment Variables\n\n`;
309
- readme += `| Variable | Description | Default |\n`;
310
- readme += `|----------|-------------|---------||\n`;
311
- readme += `| \`PORT\` | Server port | \`4000\` |\n`;
312
- readme += `| \`NODE_ENV\` | Environment | \`development\` |\n`;
313
- if (features.includes("cors")) {
314
- readme += `| \`ALLOWED_ORIGIN\` | CORS allowed origin | \`http://localhost:3000\` |\n`;
315
- }
316
- if (auth) {
317
- readme += `| \`MONGO_URI\` | MongoDB connection string | - |\n`;
318
- readme += `| \`JWT_SECRET\` | JWT secret key | - |\n`;
319
- }
320
- readme += `\n`;
321
-
322
- // Scaffold attribution
323
- readme += `\n`;
324
- readme += `## About this Scaffold\n\n`;
325
- readme += `This project was generated using the @ifecodes/backend-template scaffold. `;
326
- readme += `You can recreate or customize this scaffold using the CLI: \n\n`;
327
- readme += `- Run without installing (recommended): \`npx ifecodes-template\`\n`;
328
- readme += `- Install globally: \`npm i -g @ifecodes/backend-template\` and run \`ifecodes-template\`\n\n`;
329
-
330
- // License
331
- readme += `## License\n\n`;
332
- readme += `MIT\n`;
333
-
334
- return readme;
335
- };
1
+ export const generateReadme = (config, serviceName = null) => {
2
+ const { projectType, mode, features = [], auth, sanitizedName } = config;
3
+ const isMicroservice = projectType === "microservice";
4
+ const isTypeScript = config.language === "typescript";
5
+ const languageLabel = isTypeScript ? "TypeScript" : "JavaScript";
6
+ const monolithFileExt = isTypeScript ? "ts" : "js";
7
+
8
+ const getServices = () =>
9
+ config.allServices && config.allServices.length
10
+ ? config.allServices
11
+ : ["gateway", "health-service", ...(auth ? ["auth-service"] : [])];
12
+
13
+ const getPort = (services, serviceName, index) =>
14
+ serviceName === "gateway"
15
+ ? 4000
16
+ : 4001 +
17
+ services.filter(
18
+ (service, serviceIndex) =>
19
+ service !== "gateway" && serviceIndex < index
20
+ ).length;
21
+
22
+ let readme = `# ${serviceName || sanitizedName}\n\n`;
23
+
24
+ if (isMicroservice && serviceName) {
25
+ readme += `A microservice for ${sanitizedName}.\n\n`;
26
+ } else if (isMicroservice) {
27
+ readme += `A microservices-based backend application.\n\n`;
28
+ } else {
29
+ readme += `A monolithic backend API application.\n\n`;
30
+ }
31
+
32
+ readme += `## Architecture\n\n`;
33
+ if (isMicroservice) {
34
+ const servicesList = getServices();
35
+ readme += `- **Type**: Microservice\n`;
36
+ readme += `- **Deployment**: ${mode === "docker" ? "Docker" : "PM2"}\n`;
37
+ readme += `- **Gateway**: Port 4000 (main entry point)\n`;
38
+ readme += `- **Services**:\n`;
39
+ servicesList.forEach((service, index) => {
40
+ const pretty = service
41
+ .split("-")
42
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
43
+ .join(" ");
44
+ readme += ` - ${pretty} (port ${getPort(
45
+ servicesList,
46
+ service,
47
+ index
48
+ )})\n`;
49
+ });
50
+ readme += `\n`;
51
+ } else {
52
+ readme += `- **Type**: Monolith API\n`;
53
+ readme += `- **Port**: 4000 (default)\n\n`;
54
+ }
55
+
56
+ readme += `## Tech Stack\n\n`;
57
+ readme += `- **Runtime**: Node.js\n`;
58
+ readme += `- **Language**: ${languageLabel}\n`;
59
+ readme += `- **Framework**: Express.js\n`;
60
+ if (auth) readme += `- **Database**: MongoDB (Mongoose)\n`;
61
+ readme += `- **Operations**: Environment validation at startup and request tracing headers\n`;
62
+ if (config.validation) {
63
+ readme += `- **Validation**: Zod schema-based request validation middleware\n`;
64
+ }
65
+
66
+ if (features.length > 0 || auth) {
67
+ readme += `- **Features**:\n`;
68
+ if (features.includes("cors")) readme += ` - CORS\n`;
69
+ if (features.includes("helmet"))
70
+ readme += ` - Helmet (Security headers)\n`;
71
+ if (features.includes("rate-limit")) readme += ` - Rate Limiting\n`;
72
+ if (features.includes("morgan")) readme += ` - Morgan (HTTP logging)\n`;
73
+ if (auth) readme += ` - Authentication (JWT)\n`;
74
+ }
75
+ readme += `- **Observability**: Request IDs and HTTP access logs\n`;
76
+ readme += `- **API Docs**: OpenAPI Swagger UI (/api-docs)\n`;
77
+ if (config.cicd) readme += `- **CI/CD**: GitHub Actions starter workflow\n`;
78
+ readme += `\n`;
79
+
80
+ readme += `## Getting Started\n\n`;
81
+ readme += `### Prerequisites\n\n`;
82
+ readme += `- Node.js (v18 or higher)\n`;
83
+ readme += `- npm or yarn\n`;
84
+ if (auth) readme += `- MongoDB\n`;
85
+ if (isMicroservice && mode === "docker")
86
+ readme += `- Docker & Docker Compose\n`;
87
+ if (isMicroservice && mode === "nodocker")
88
+ readme += `- PM2 (\`npm install -g pm2\`)\n`;
89
+ readme += `\n`;
90
+
91
+ readme += `### Installation\n\n`;
92
+ readme += `1. Clone the repository\n`;
93
+ readme += `\`\`\`bash\n`;
94
+ readme += `cd ${sanitizedName}\n`;
95
+ readme += `\`\`\`\n\n`;
96
+
97
+ if (isMicroservice) {
98
+ const servicesList = getServices();
99
+ readme += `2. Install dependencies for all services\n`;
100
+ readme += `\`\`\`bash\n`;
101
+ readme += `# Install root dependencies (Husky)\n`;
102
+ readme += `npm install\n\n`;
103
+ readme += `# Install dependencies for each service\n`;
104
+ servicesList.forEach((service) => {
105
+ if (
106
+ service === "gateway" ||
107
+ service === "health-service" ||
108
+ service === "auth-service"
109
+ ) {
110
+ readme += `cd services/${service} && npm install && cd ../..\n`;
111
+ }
112
+ });
113
+ readme += `\`\`\`\n\n`;
114
+ } else {
115
+ readme += `2. Install dependencies\n`;
116
+ readme += `\`\`\`bash\n`;
117
+ readme += `npm install\n`;
118
+ readme += `\`\`\`\n\n`;
119
+ }
120
+
121
+ readme += `3. Set up environment variables\n`;
122
+ if (isMicroservice) {
123
+ readme += `\`\`\`bash\n`;
124
+ readme += `# Environment variables are configured in docker-compose.yml or pm2.config.js\n`;
125
+ readme += `# No .env files needed for individual services\n`;
126
+ readme += `\`\`\`\n\n`;
127
+ } else {
128
+ readme += `\`\`\`bash\n`;
129
+ readme += `cp .env.example .env\n`;
130
+ readme += `\`\`\`\n\n`;
131
+ }
132
+
133
+ if (auth) {
134
+ readme += `4. Configure your MongoDB connection and JWT secret in the \`.env\` file${
135
+ isMicroservice ? "s" : ""
136
+ }\n\n`;
137
+ }
138
+
139
+ readme += `## Running the Application\n\n`;
140
+ if (isMicroservice && mode === "docker") {
141
+ readme += `### With Docker\n\n`;
142
+ readme += `\`\`\`bash\n`;
143
+ readme += `# Start all services\n`;
144
+ readme += `npm run dev\n\n`;
145
+ readme += `# Start in detached mode\n`;
146
+ readme += `npm run dev -d\n\n`;
147
+ readme += `# Stop all services\n`;
148
+ readme += `npm stop\n`;
149
+ readme += `\`\`\`\n\n`;
150
+ } else if (isMicroservice && mode === "nodocker") {
151
+ readme += `### With PM2\n\n`;
152
+ readme += `\`\`\`bash\n`;
153
+ readme += `# Start all services\n`;
154
+ readme += `pm2 start pm2.config.js\n\n`;
155
+ readme += `# View logs\n`;
156
+ readme += `pm2 logs\n\n`;
157
+ readme += `# Stop all services\n`;
158
+ readme += `pm2 stop all\n\n`;
159
+ readme += `# Delete all services\n`;
160
+ readme += `pm2 delete all\n`;
161
+ readme += `\`\`\`\n\n`;
162
+ } else {
163
+ readme += `### Development\n\n`;
164
+ readme += `\`\`\`bash\n`;
165
+ readme += `npm run dev\n`;
166
+ readme += `\`\`\`\n\n`;
167
+ if (isTypeScript) {
168
+ readme += `### Production\n\n`;
169
+ readme += `\`\`\`bash\n`;
170
+ readme += `npm run build\n`;
171
+ readme += `npm start\n`;
172
+ readme += `\`\`\`\n\n`;
173
+ }
174
+ }
175
+
176
+ readme += `## API Endpoints\n\n`;
177
+ if (isMicroservice) {
178
+ const servicesList = getServices();
179
+ readme += `All requests go through the API Gateway at \`http://localhost:4000\`\n\n`;
180
+ readme += `### Gateway Endpoints\n`;
181
+ readme += `- **GET** \`/\` - API information and available endpoints\n`;
182
+ readme += `- **GET** \`/health\` - Gateway health check\n\n`;
183
+ readme += `- **GET** \`/api-docs\` - Interactive Swagger UI\n\n`;
184
+ readme += `### Health Service (Proxied through Gateway)\n`;
185
+ readme += `- **GET** \`/api/v1/health\` - Service health check with system metrics\n`;
186
+ readme += ` - Returns: status, uptime, timestamp, memory usage\n\n`;
187
+ if (auth) {
188
+ readme += `### Auth Service (Proxied through Gateway)\n`;
189
+ readme += `- **POST** \`/api/v1/auth/register\` - Register a new user\n`;
190
+ readme += `- **POST** \`/api/v1/auth/login\` - Login user\n\n`;
191
+ }
192
+
193
+ readme += `### Direct Service Access (Development Only)\n`;
194
+ servicesList.forEach((service, index) => {
195
+ const port = getPort(servicesList, service, index);
196
+ const basePath = service === "gateway" ? `` : `/api/v1`;
197
+ readme += `- **${service}**: \`http://localhost:${port}${basePath}\`\n`;
198
+ });
199
+ readme += `\n`;
200
+
201
+ readme += `### Example Requests\n`;
202
+ readme += `\`\`\`bash\n`;
203
+ readme += `# Gateway info\n`;
204
+ readme += `curl http://localhost:4000/\n\n`;
205
+ readme += `# Gateway health\n`;
206
+ readme += `curl http://localhost:4000/health\n\n`;
207
+
208
+ servicesList.forEach((service, index) => {
209
+ if (service === "gateway") return;
210
+ const port = getPort(servicesList, service, index);
211
+ readme += `# ${service} (direct access)\n`;
212
+ readme += `curl http://localhost:${port}/api/v1/health\n\n`;
213
+ });
214
+
215
+ if (auth && servicesList.includes("auth-service")) {
216
+ const authPort = getPort(
217
+ servicesList,
218
+ "auth-service",
219
+ servicesList.indexOf("auth-service")
220
+ );
221
+ readme += `# Auth requests (through gateway)\n`;
222
+ readme += `curl -X POST http://localhost:4000/api/v1/auth/register \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
223
+ readme += `curl -X POST http://localhost:4000/api/v1/auth/login \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
224
+ readme += `# Auth requests (direct access)\n`;
225
+ readme += `curl -X POST http://localhost:${authPort}/api/v1/auth/register \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
226
+ readme += `curl -X POST http://localhost:${authPort}/api/v1/auth/login \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
227
+ }
228
+ readme += `\`\`\`\n\n`;
229
+ } else {
230
+ readme += `Base URL: \`http://localhost:4000\`\n\n`;
231
+ readme += `- **GET** \`/\` - Root endpoint (API info)\n`;
232
+ readme += `- **GET** \`/api/v1/health\` - Health check\n\n`;
233
+ readme += `- **GET** \`/api-docs\` - Interactive Swagger UI\n\n`;
234
+ if (auth) {
235
+ readme += `### Authentication\n`;
236
+ readme += `- **POST** \`/api/v1/auth/register\` - Register a new user\n`;
237
+ readme += `- **POST** \`/api/v1/auth/login\` - Login user\n\n`;
238
+ }
239
+
240
+ readme += `### Example Requests\n`;
241
+ readme += `\`\`\`bash\n`;
242
+ readme += `# Root info\n`;
243
+ readme += `curl http://localhost:4000/\n\n`;
244
+ readme += `# Health check\n`;
245
+ readme += `curl http://localhost:4000/api/v1/health\n\n`;
246
+ if (auth) {
247
+ readme += `# Register user\n`;
248
+ readme += `curl -X POST http://localhost:4000/api/v1/auth/register \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
249
+ readme += `# Login user\n`;
250
+ readme += `curl -X POST http://localhost:4000/api/v1/auth/login \\\n+ -H "Content-Type: application/json" \\\n+ -d '{"username":"testuser","password":"password123"}'\n\n`;
251
+ }
252
+ readme += `\`\`\`\n\n`;
253
+ }
254
+
255
+ readme += `## Project Structure\n\n`;
256
+ readme += `\`\`\`\n`;
257
+ if (isMicroservice) {
258
+ const servicesList = getServices();
259
+ readme += `${sanitizedName}/\n`;
260
+ readme += `├── shared/ # Shared utilities across services\n`;
261
+ readme += `│ ├── config/ # Database, environment configs\n`;
262
+ readme += `│ └── utils/ # Logger, error handlers\n`;
263
+ readme += `├── services/\n`;
264
+ servicesList.forEach((service) => {
265
+ readme += `│ ├── ${service}/\n`;
266
+ });
267
+ readme += `├── ${
268
+ mode === "docker" ? "docker-compose.yml" : "pm2.config.js"
269
+ }\n`;
270
+ readme += `├── .husky/ # Git hooks\n`;
271
+ readme += `└── package.json # Root package.json\n`;
272
+ } else {
273
+ readme += `${sanitizedName}/\n`;
274
+ if (config.cicd) {
275
+ readme += `├── .github/\n`;
276
+ readme += `│ ├── workflows/\n`;
277
+ readme += `│ │ └── ci-cd.yml # GitHub Actions CI/CD workflow\n`;
278
+ readme += `│ └── pull_request_template.md # PR template\n`;
279
+ }
280
+ readme += `├── .husky/ # Git hooks\n`;
281
+ readme += `├── src/\n`;
282
+ readme += `│ ├── config/ # Configuration files\n`;
283
+ readme += `│ ├── middlewares/ # Custom middlewares\n`;
284
+ readme += `│ ├── modules/ # Feature modules\n`;
285
+ readme += `│ │ └── v1/ # API version 1\n`;
286
+ if (auth) readme += `│ │ ├── auth/ # Auth module\n`;
287
+ readme += `│ │ └── health/ # Health check\n`;
288
+ if (auth) readme += `│ ├── models/ # Database models\n`;
289
+ readme += `│ ├── utils/ # Utility functions\n`;
290
+ readme += `│ ├── app.${monolithFileExt} # Express app setup\n`;
291
+ readme += `│ ├── routes.${monolithFileExt} # Route definitions\n`;
292
+ readme += `│ └── server.${monolithFileExt} # Server entry point\n`;
293
+ readme += `├── .husky/ # Git hooks\n`;
294
+ if (config.cicd)
295
+ readme += `├── CONTRIBUTING.md # Contribution guidelines\n`;
296
+ readme += `├── package.json\n`;
297
+ if (isTypeScript) readme += `└── tsconfig.json\n`;
298
+ }
299
+ readme += `\`\`\`\n\n`;
300
+
301
+ readme += `## Available Scripts\n\n`;
302
+ if (isMicroservice) {
303
+ if (mode === "docker") {
304
+ readme += `- \`npm run dev\` - Start all services\n`;
305
+ readme += `- \`npm stop\` - Stop all services\n`;
306
+ readme += `- \`docker-compose logs -f [service-name]\` - View service logs\n`;
307
+ } else {
308
+ readme += `- \`pm2 start pm2.config.js\` - Start all services\n`;
309
+ readme += `- \`pm2 logs\` - View all service logs\n`;
310
+ readme += `- \`pm2 monit\` - Monitor services\n`;
311
+ readme += `- \`pm2 stop all\` - Stop all services\n`;
312
+ }
313
+ } else {
314
+ readme += `- \`npm run dev\` - Start development server with hot reload\n`;
315
+ if (isTypeScript) readme += `- \`npm run build\` - Build for production\n`;
316
+ readme += `- \`npm start\` - Start production server\n`;
317
+ readme += `- \`npm run lint\` - Run ESLint\n`;
318
+ readme += `- \`npm run format\` - Run Prettier\n`;
319
+ }
320
+ readme += `\n`;
321
+
322
+ readme += `## Environment Variables\n\n`;
323
+ readme += `| Variable | Description | Default |\n`;
324
+ readme += `| --- | --- | --- |\n`;
325
+ if (isMicroservice) {
326
+ const envServices = getServices();
327
+ envServices.forEach((service, index) => {
328
+ const port = getPort(envServices, service, index);
329
+ const envVarName = `${service.toUpperCase().replace(/-/g, "_")}_PORT`;
330
+ const description = `${service.replace(/-/g, " ")} service port`;
331
+ readme += `| \`${envVarName}\` | ${description} | \`${port}\` |\n`;
332
+ });
333
+ } else {
334
+ readme += `| \`PORT\` | Server port | \`4000\` |\n`;
335
+ }
336
+ readme += `| \`NODE_ENV\` | Environment | \`development\` |\n`;
337
+ if (features.includes("cors")) {
338
+ readme += `| \`ALLOWED_ORIGIN\` | CORS allowed origin | \`http://localhost:3000\` |\n`;
339
+ }
340
+ if (auth) {
341
+ readme += `| \`MONGO_URI\` | MongoDB connection string | - |\n`;
342
+ readme += `| \`JWT_SECRET\` | JWT secret key | - |\n`;
343
+ }
344
+ readme += `\n`;
345
+
346
+ if (config.cicd && !isMicroservice) {
347
+ readme += `## Generated Files\n\n`;
348
+ readme += `The following files have been automatically generated for team projects:\n\n`;
349
+ readme += `- \`.github/workflows/ci-cd.yml\` - GitHub Actions CI/CD workflow\n`;
350
+ readme += `- \`.github/pull_request_template.md\` - Pull request template\n`;
351
+ readme += `- \`CONTRIBUTING.md\` - Contribution guidelines\n\n`;
352
+ readme += `These files are ready to use and can be customized for your team's needs.\n\n`;
353
+ }
354
+
355
+ readme += `## About this Scaffold\n\n`;
356
+ readme += `This project was generated using the @ifecodes/backend-template scaffold. `;
357
+ readme += `You can recreate or customize this scaffold using the CLI:\n\n`;
358
+ readme += `- Run without installing (recommended): \`npx ifecodes-template\`\n`;
359
+ readme += `- Install globally: \`npm i -g @ifecodes/backend-template\` and run \`ifecodes-template\`\n\n`;
360
+
361
+ readme += `## License\n\nMIT\n`;
362
+
363
+ return readme;
364
+ };