express-genix 1.1.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +204 -259
  3. package/index.js +229 -113
  4. package/lib/cleanup.js +41 -129
  5. package/lib/features.js +239 -0
  6. package/lib/generator.js +286 -204
  7. package/lib/utils.js +43 -91
  8. package/package.json +81 -63
  9. package/templates/cicd/github-actions.yml.ejs +70 -0
  10. package/templates/config/database.mongo.js.ejs +29 -33
  11. package/templates/config/database.postgres.js.ejs +41 -40
  12. package/templates/config/database.prisma.js.ejs +26 -0
  13. package/templates/config/redis.js.ejs +28 -0
  14. package/templates/config/schema.prisma.ejs +20 -0
  15. package/templates/config/swagger.js.ejs +30 -0
  16. package/templates/config/websocket.js.ejs +62 -0
  17. package/templates/controllers/authController.js.ejs +152 -129
  18. package/templates/controllers/exampleController.js.ejs +92 -152
  19. package/templates/controllers/userController.js.ejs +52 -60
  20. package/templates/core/Dockerfile.ejs +41 -31
  21. package/templates/core/README.md.ejs +191 -179
  22. package/templates/core/app.js.ejs +114 -64
  23. package/templates/core/docker-compose.yml.ejs +59 -47
  24. package/templates/core/dockerignore.ejs +7 -0
  25. package/templates/core/env.ejs +25 -19
  26. package/templates/core/env.example.ejs +26 -0
  27. package/templates/core/eslintrc.json.ejs +50 -20
  28. package/templates/core/gitignore.ejs +51 -51
  29. package/templates/core/healthcheck.js.ejs +24 -24
  30. package/templates/core/jest.config.js.ejs +19 -22
  31. package/templates/core/package.json.ejs +70 -33
  32. package/templates/core/prettierrc.json.ejs +11 -11
  33. package/templates/core/server.js.ejs +64 -48
  34. package/templates/core/tsconfig.json.ejs +19 -0
  35. package/templates/middleware/auth.js.ejs +80 -66
  36. package/templates/middleware/cache.js.ejs +67 -0
  37. package/templates/middleware/errorHandler.js.ejs +50 -46
  38. package/templates/middleware/requestId.js.ejs +9 -0
  39. package/templates/middleware/validation.js.ejs +109 -47
  40. package/templates/migrations/create-users.js.ejs +50 -0
  41. package/templates/migrations/seed-users.js.ejs +34 -0
  42. package/templates/migrations/sequelizerc.ejs +8 -0
  43. package/templates/models/User.mongo.js.ejs +29 -29
  44. package/templates/models/User.postgres.js.ejs +40 -40
  45. package/templates/models/index.mongo.js.ejs +7 -7
  46. package/templates/models/index.postgres.js.ejs +11 -11
  47. package/templates/routes/authRoutes.js.ejs +222 -13
  48. package/templates/routes/exampleRoutes.js.ejs +100 -12
  49. package/templates/routes/index.js.ejs +34 -24
  50. package/templates/routes/userRoutes.js.ejs +78 -15
  51. package/templates/services/authService.js.ejs +111 -35
  52. package/templates/services/exampleService.js.ejs +112 -112
  53. package/templates/services/userService.mongodb.js.ejs +33 -33
  54. package/templates/services/userService.postgres.js.ejs +30 -30
  55. package/templates/services/userService.prisma.js.ejs +36 -0
  56. package/templates/tests/auth.test.js.ejs +83 -66
  57. package/templates/tests/example.test.js.ejs +109 -112
  58. package/templates/tests/setup.js.ejs +11 -11
  59. package/templates/tests/users.test.js.ejs +42 -42
  60. package/templates/utils/envValidator.js.ejs +23 -0
  61. package/templates/utils/errors.js.ejs +12 -12
  62. package/templates/utils/logger.js.ejs +37 -28
  63. package/templates/utils/response.js.ejs +28 -0
  64. package/templates/utils/validators.js.ejs +34 -34
  65. package/templates/config/swagger.json.ejs +0 -194
  66. package/templates/core/index.js.ejs +0 -24
package/package.json CHANGED
@@ -1,63 +1,81 @@
1
- {
2
- "name": "express-genix",
3
- "version": "1.1.4",
4
- "description": "Production-grade CLI to generate Express apps with JWT, DB, rate-limiting, automatic formatting, and more",
5
- "main": "index.js",
6
- "bin": {
7
- "express-genix": "./index.js"
8
- },
9
- "scripts": {
10
- "start": "node index.js",
11
- "test": "jest",
12
- "lint": "eslint .",
13
- "lint:fix": "eslint . --fix"
14
- },
15
- "dependencies": {
16
- "commander": "^12.1.0",
17
- "inquirer": "^10.2.0",
18
- "ejs": "^3.1.9"
19
- },
20
- "devDependencies": {
21
- "jest": "^29.7.0",
22
- "eslint": "^8.56.0",
23
- "eslint-config-airbnb-base": "^15.0.0",
24
- "eslint-plugin-import": "^2.29.1"
25
- },
26
- "keywords": [
27
- "express",
28
- "cli",
29
- "boilerplate",
30
- "jwt",
31
- "rate-limiting",
32
- "production",
33
- "prettier",
34
- "eslint",
35
- "mongodb",
36
- "postgresql",
37
- "api",
38
- "nodejs",
39
- "javascript",
40
- "backend",
41
- "rest-api",
42
- "microservices"
43
- ],
44
- "author": "Joshua Maeba Nyamasege",
45
- "license": "MIT",
46
- "repository": {
47
- "type": "git",
48
- "url": "git+https://github.com/LambdaAI001/express-genix-CLI.git"
49
- },
50
- "bugs": {
51
- "url": "https://github.com/LambdaAI001/express-genix-CLI/issues"
52
- },
53
- "homepage": "https://github.com/LambdaAI001/express-genix-CLI#readme",
54
- "engines": {
55
- "node": ">=16.0.0"
56
- },
57
- "files": [
58
- "index.js",
59
- "lib/",
60
- "templates/",
61
- "README.md"
62
- ]
63
- }
1
+ {
2
+ "name": "express-genix",
3
+ "version": "2.0.0",
4
+ "description": "Production-grade CLI to generate Express apps with JWT, TypeScript, Prisma, MongoDB, PostgreSQL, rate-limiting, Swagger, CI/CD, and more",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "express-genix": "./index.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node index.js",
11
+ "test": "jest",
12
+ "lint": "eslint .",
13
+ "lint:fix": "eslint . --fix"
14
+ },
15
+ "jest": {
16
+ "testPathIgnorePatterns": [
17
+ "/node_modules/",
18
+ "<rootDir>/test-app/",
19
+ "<rootDir>/(?!tests/)"
20
+ ],
21
+ "testMatch": [
22
+ "<rootDir>/tests/**/*.test.js"
23
+ ]
24
+ },
25
+ "dependencies": {
26
+ "commander": "^12.1.0",
27
+ "inquirer": "^10.2.0",
28
+ "ejs": "^3.1.9"
29
+ },
30
+ "devDependencies": {
31
+ "jest": "^29.7.0",
32
+ "eslint": "^8.56.0",
33
+ "eslint-config-airbnb-base": "^15.0.0",
34
+ "eslint-plugin-import": "^2.29.1"
35
+ },
36
+ "keywords": [
37
+ "express",
38
+ "cli",
39
+ "boilerplate",
40
+ "generator",
41
+ "scaffold",
42
+ "typescript",
43
+ "prisma",
44
+ "jwt",
45
+ "rate-limiting",
46
+ "swagger",
47
+ "openapi",
48
+ "production",
49
+ "prettier",
50
+ "eslint",
51
+ "mongodb",
52
+ "postgresql",
53
+ "api",
54
+ "nodejs",
55
+ "javascript",
56
+ "backend",
57
+ "rest-api",
58
+ "microservices",
59
+ "docker",
60
+ "cicd"
61
+ ],
62
+ "author": "Joshua Maeba Nyamasege",
63
+ "license": "MIT",
64
+ "repository": {
65
+ "type": "git",
66
+ "url": "git+https://github.com/LambdaAI001/express-genix-CLI.git"
67
+ },
68
+ "bugs": {
69
+ "url": "https://github.com/LambdaAI001/express-genix-CLI/issues"
70
+ },
71
+ "homepage": "https://github.com/LambdaAI001/express-genix-CLI#readme",
72
+ "engines": {
73
+ "node": ">=18.0.0"
74
+ },
75
+ "files": [
76
+ "index.js",
77
+ "lib/",
78
+ "templates/",
79
+ "README.md"
80
+ ]
81
+ }
@@ -0,0 +1,70 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, develop]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+
13
+ strategy:
14
+ matrix:
15
+ node-version: [18.x, 20.x]
16
+ <% if (db === 'mongodb') { %>
17
+ services:
18
+ mongodb:
19
+ image: mongo:7
20
+ ports:
21
+ - 27017:27017
22
+ <% } else if (hasDatabase) { %>
23
+ services:
24
+ postgres:
25
+ image: postgres:15-alpine
26
+ env:
27
+ POSTGRES_DB: <%= projectName %>_test
28
+ POSTGRES_USER: postgres
29
+ POSTGRES_PASSWORD: postgres
30
+ ports:
31
+ - 5432:5432
32
+ options: >-
33
+ --health-cmd pg_isready
34
+ --health-interval 10s
35
+ --health-timeout 5s
36
+ --health-retries 5
37
+ <% } %>
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+
41
+ - name: Use Node.js ${{ matrix.node-version }}
42
+ uses: actions/setup-node@v4
43
+ with:
44
+ node-version: ${{ matrix.node-version }}
45
+ cache: 'npm'
46
+
47
+ - name: Install dependencies
48
+ run: npm ci
49
+
50
+ - name: Lint
51
+ run: npm run lint
52
+
53
+ - name: Check formatting
54
+ run: npm run format:check
55
+ <% if (isTypescript) { %>
56
+ - name: Type check
57
+ run: npx tsc --noEmit
58
+ <% } %><% if (isPrisma) { %>
59
+ - name: Generate Prisma client
60
+ run: npx prisma generate
61
+ <% } %>
62
+ - name: Run tests
63
+ run: npm test
64
+ env:
65
+ NODE_ENV: test
66
+ <% if (hasAuth) { %> JWT_SECRET: ci-test-secret
67
+ JWT_REFRESH_SECRET: ci-test-refresh-secret
68
+ <% } %><% if (db === 'mongodb') { %> MONGO_URI: mongodb://localhost:27017/<%= projectName %>_test
69
+ <% } else if (hasDatabase) { %> DATABASE_URL: postgresql://postgres:postgres@localhost:5432/<%= projectName %>_test
70
+ <% } %>
@@ -1,34 +1,30 @@
1
- const mongoose = require('mongoose');
2
-
3
- const mongoUri = process.env.MONGO_URI || 'mongodb://localhost:27017/<%= projectName %>';
4
-
5
- const connect = async () => {
6
- try {
7
- // Remove deprecated options - they're no longer needed in modern Mongoose
8
- await mongoose.connect(mongoUri);
9
- console.log('MongoDB connected successfully');
10
- } catch (error) {
11
- console.error('MongoDB connection error:', error);
12
- process.exit(1);
13
- }
14
- };
15
-
16
- const disconnect = async () => {
17
- try {
18
- await mongoose.disconnect();
19
- console.log('MongoDB disconnected');
20
- } catch (error) {
21
- console.error('MongoDB disconnection error:', error);
22
- }
23
- };
24
-
25
- // Handle connection events
26
- mongoose.connection.on('error', (error) => {
27
- console.error('MongoDB connection error:', error);
28
- });
29
-
30
- mongoose.connection.on('disconnected', () => {
31
- console.log('MongoDB disconnected');
32
- });
33
-
1
+ const mongoose = require('mongoose');
2
+ const { createLogger } = require('../utils/logger');
3
+
4
+ const logger = createLogger('Database');
5
+ const mongoUri = process.env.MONGO_URI || 'mongodb://localhost:27017/<%= projectName %>';
6
+
7
+ const connect = async () => {
8
+ try {
9
+ await mongoose.connect(mongoUri);
10
+ logger.info('MongoDB connected successfully');
11
+ } catch (error) {
12
+ logger.error('MongoDB connection error:', error);
13
+ process.exit(1);
14
+ }
15
+ };
16
+
17
+ const disconnect = async () => {
18
+ try {
19
+ await mongoose.disconnect();
20
+ logger.info('MongoDB disconnected');
21
+ } catch (error) {
22
+ logger.error('MongoDB disconnection error:', error);
23
+ }
24
+ };
25
+
26
+ mongoose.connection.on('error', (error) => {
27
+ logger.error('MongoDB connection error:', error);
28
+ });
29
+
34
30
  module.exports = { connect, disconnect };
@@ -1,41 +1,42 @@
1
- const { Sequelize } = require('sequelize');
2
-
3
- const databaseUrl = process.env.DATABASE_URL || 'postgresql://user:password@localhost:5432/<%= projectName %>';
4
-
5
- const sequelize = new Sequelize(databaseUrl, {
6
- dialect: 'postgres',
7
- logging: process.env.NODE_ENV === 'development' ? console.log : false,
8
- pool: {
9
- max: 5,
10
- min: 0,
11
- acquire: 30000,
12
- idle: 10000,
13
- },
14
- });
15
-
16
- const connect = async () => {
17
- try {
18
- await sequelize.authenticate();
19
- console.log('PostgreSQL connected successfully');
20
-
21
- // Sync database in development
22
- if (process.env.NODE_ENV === 'development') {
23
- await sequelize.sync({ alter: true });
24
- console.log('Database synced');
25
- }
26
- } catch (error) {
27
- console.error('PostgreSQL connection error:', error);
28
- process.exit(1);
29
- }
30
- };
31
-
32
- const disconnect = async () => {
33
- try {
34
- await sequelize.close();
35
- console.log('PostgreSQL disconnected');
36
- } catch (error) {
37
- console.error('PostgreSQL disconnection error:', error);
38
- }
39
- };
40
-
1
+ const { Sequelize } = require('sequelize');
2
+ const { createLogger } = require('../utils/logger');
3
+
4
+ const logger = createLogger('Database');
5
+ const databaseUrl = process.env.DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/<%= projectName %>';
6
+
7
+ const sequelize = new Sequelize(databaseUrl, {
8
+ dialect: 'postgres',
9
+ logging: process.env.NODE_ENV === 'development' ? (msg) => logger.debug(msg) : false,
10
+ pool: {
11
+ max: 5,
12
+ min: 0,
13
+ acquire: 30000,
14
+ idle: 10000,
15
+ },
16
+ });
17
+
18
+ const connect = async () => {
19
+ try {
20
+ await sequelize.authenticate();
21
+ logger.info('PostgreSQL connected successfully');
22
+
23
+ if (process.env.NODE_ENV === 'development') {
24
+ await sequelize.sync({ alter: true });
25
+ logger.info('Database synced');
26
+ }
27
+ } catch (error) {
28
+ logger.error('PostgreSQL connection error:', error);
29
+ process.exit(1);
30
+ }
31
+ };
32
+
33
+ const disconnect = async () => {
34
+ try {
35
+ await sequelize.close();
36
+ logger.info('PostgreSQL disconnected');
37
+ } catch (error) {
38
+ logger.error('PostgreSQL disconnection error:', error);
39
+ }
40
+ };
41
+
41
42
  module.exports = { connect, disconnect, sequelize };
@@ -0,0 +1,26 @@
1
+ const { PrismaClient } = require('@prisma/client');
2
+
3
+ const prisma = new PrismaClient({
4
+ log: process.env.NODE_ENV === 'development' ? ['query', 'info', 'warn', 'error'] : ['error'],
5
+ });
6
+
7
+ const connect = async () => {
8
+ try {
9
+ await prisma.$connect();
10
+ console.log('PostgreSQL (Prisma) connected successfully');
11
+ } catch (error) {
12
+ console.error('Database connection error:', error);
13
+ process.exit(1);
14
+ }
15
+ };
16
+
17
+ const disconnect = async () => {
18
+ try {
19
+ await prisma.$disconnect();
20
+ console.log('PostgreSQL (Prisma) disconnected');
21
+ } catch (error) {
22
+ console.error('Database disconnection error:', error);
23
+ }
24
+ };
25
+
26
+ module.exports = { prisma, connect, disconnect };
@@ -0,0 +1,28 @@
1
+ const Redis = require('ioredis');
2
+ const logger = require('../utils/logger');
3
+
4
+ const redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379', {
5
+ maxRetriesPerRequest: 3,
6
+ retryStrategy(times) {
7
+ if (times > 10) {
8
+ logger.error('Redis: max retries reached, giving up');
9
+ return null;
10
+ }
11
+ return Math.min(times * 200, 5000);
12
+ },
13
+ lazyConnect: true,
14
+ });
15
+
16
+ redis.on('connect', () => logger.info('Redis connected'));
17
+ redis.on('error', (err) => logger.error('Redis error', { error: err.message }));
18
+
19
+ const connectRedis = async () => {
20
+ try {
21
+ await redis.connect();
22
+ } catch (err) {
23
+ logger.error('Failed to connect to Redis', { error: err.message });
24
+ throw err;
25
+ }
26
+ };
27
+
28
+ module.exports = { redis, connectRedis };
@@ -0,0 +1,20 @@
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ }
4
+
5
+ datasource db {
6
+ provider = "postgresql"
7
+ url = env("DATABASE_URL")
8
+ }
9
+ <% if (hasAuth) { %>
10
+ model User {
11
+ id String @id @default(uuid())
12
+ username String @unique @db.VarChar(50)
13
+ email String @unique
14
+ password String
15
+ createdAt DateTime @default(now()) @map("created_at")
16
+ updatedAt DateTime @updatedAt @map("updated_at")
17
+
18
+ @@map("users")
19
+ }
20
+ <% } %>
@@ -0,0 +1,30 @@
1
+ const swaggerJsdoc = require('swagger-jsdoc');
2
+
3
+ const options = {
4
+ definition: {
5
+ openapi: '3.0.0',
6
+ info: {
7
+ title: '<%= projectName %> API',
8
+ version: '1.0.0',
9
+ description: 'A production-grade Express.js API<% if (hasAuth) { %> with JWT authentication<% } %>',
10
+ },
11
+ servers: [
12
+ {
13
+ url: `http://localhost:${process.env.PORT || 3000}/api`,
14
+ description: 'Development server',
15
+ },
16
+ ],<% if (hasAuth) { %>
17
+ components: {
18
+ securitySchemes: {
19
+ bearerAuth: {
20
+ type: 'http',
21
+ scheme: 'bearer',
22
+ bearerFormat: 'JWT',
23
+ },
24
+ },
25
+ },<% } %>
26
+ },
27
+ apis: ['./src/routes/*.js'<% if (isTypescript) { %>, './src/routes/*.ts'<% } %>],
28
+ };
29
+
30
+ module.exports = swaggerJsdoc(options);
@@ -0,0 +1,62 @@
1
+ const { Server } = require('socket.io');
2
+ const { createLogger } = require('../utils/logger');
3
+
4
+ const logger = createLogger('WebSocket');
5
+
6
+ const setupWebSocket = (httpServer) => {
7
+ const io = new Server(httpServer, {
8
+ cors: {
9
+ origin: process.env.CORS_ORIGIN || '*',
10
+ methods: ['GET', 'POST'],
11
+ },
12
+ });
13
+
14
+ io.on('connection', (socket) => {
15
+ logger.info('Client connected', { socketId: socket.id });
16
+
17
+ socket.on('ping', (data) => {
18
+ socket.emit('pong', { received: data, timestamp: new Date().toISOString() });
19
+ });
20
+
21
+ socket.on('join-room', (room) => {
22
+ socket.join(room);
23
+ logger.info('Client joined room', { socketId: socket.id, room });
24
+ socket.to(room).emit('user-joined', { socketId: socket.id, room });
25
+ });
26
+
27
+ socket.on('leave-room', (room) => {
28
+ socket.leave(room);
29
+ logger.info('Client left room', { socketId: socket.id, room });
30
+ socket.to(room).emit('user-left', { socketId: socket.id, room });
31
+ });
32
+
33
+ socket.on('message', (data) => {
34
+ if (data.room) {
35
+ socket.to(data.room).emit('message', {
36
+ from: socket.id,
37
+ content: data.content,
38
+ timestamp: new Date().toISOString(),
39
+ });
40
+ } else {
41
+ socket.broadcast.emit('message', {
42
+ from: socket.id,
43
+ content: data.content,
44
+ timestamp: new Date().toISOString(),
45
+ });
46
+ }
47
+ });
48
+
49
+ socket.on('disconnect', (reason) => {
50
+ logger.info('Client disconnected', { socketId: socket.id, reason });
51
+ });
52
+
53
+ socket.on('error', (error) => {
54
+ logger.error('Socket error', { socketId: socket.id, error: error.message });
55
+ });
56
+ });
57
+
58
+ logger.info('WebSocket server initialized');
59
+ return io;
60
+ };
61
+
62
+ module.exports = { setupWebSocket };