nodejs-quickstart-structure 1.1.6
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.
- package/README.md +98 -0
- package/bin/index.js +66 -0
- package/docs/demo.gif +0 -0
- package/docs/generateCase.md +58 -0
- package/lib/generator.js +315 -0
- package/lib/prompts.js +76 -0
- package/package.json +40 -0
- package/templates/clean-architecture/js/src/domain/models/User.js +9 -0
- package/templates/clean-architecture/js/src/domain/repositories/UserRepository.js +9 -0
- package/templates/clean-architecture/js/src/index.js.ejs +37 -0
- package/templates/clean-architecture/js/src/infrastructure/log/logger.js +22 -0
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +19 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +31 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.js +23 -0
- package/templates/clean-architecture/js/src/interfaces/controllers/UserController.js +30 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.js +77 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.js +14 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +12 -0
- package/templates/clean-architecture/js/src/utils/httpCodes.js +9 -0
- package/templates/clean-architecture/ts/src/config/swagger.ts.ejs +24 -0
- package/templates/clean-architecture/ts/src/domain/user.ts +7 -0
- package/templates/clean-architecture/ts/src/index.ts.ejs +71 -0
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +22 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.ts.ejs +18 -0
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts +45 -0
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +76 -0
- package/templates/clean-architecture/ts/src/usecases/createUser.ts +13 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +10 -0
- package/templates/clean-architecture/ts/src/utils/httpCodes.ts +7 -0
- package/templates/common/.dockerignore +10 -0
- package/templates/common/.eslintrc.json.ejs +26 -0
- package/templates/common/.lintstagedrc +6 -0
- package/templates/common/Dockerfile +49 -0
- package/templates/common/README.md.ejs +87 -0
- package/templates/common/_gitignore +5 -0
- package/templates/common/database/js/database.js.ejs +20 -0
- package/templates/common/database/js/models/User.js.ejs +30 -0
- package/templates/common/database/ts/database.ts.ejs +22 -0
- package/templates/common/database/ts/models/User.ts.ejs +34 -0
- package/templates/common/docker-compose.yml.ejs +93 -0
- package/templates/common/jest.config.js.ejs +11 -0
- package/templates/common/kafka/js/config/kafka.js +8 -0
- package/templates/common/kafka/js/services/kafkaService.js +29 -0
- package/templates/common/kafka/ts/config/kafka.ts +6 -0
- package/templates/common/kafka/ts/services/kafkaService.ts +36 -0
- package/templates/common/package.json.ejs +78 -0
- package/templates/common/tsconfig.json +19 -0
- package/templates/common/views/ejs/index.ejs +31 -0
- package/templates/common/views/pug/index.pug +26 -0
- package/templates/db/mysql/V1__Initial_Setup.sql +9 -0
- package/templates/db/postgres/V1__Initial_Setup.sql +9 -0
- package/templates/mvc/js/src/config/database.js +12 -0
- package/templates/mvc/js/src/config/swagger.js +23 -0
- package/templates/mvc/js/src/controllers/userController.js +14 -0
- package/templates/mvc/js/src/controllers/userController.js.ejs +23 -0
- package/templates/mvc/js/src/index.js.ejs +81 -0
- package/templates/mvc/js/src/routes/api.js +74 -0
- package/templates/mvc/js/src/utils/httpCodes.js +9 -0
- package/templates/mvc/js/src/utils/logger.js +30 -0
- package/templates/mvc/ts/src/config/swagger.ts.ejs +24 -0
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +32 -0
- package/templates/mvc/ts/src/index.ts.ejs +89 -0
- package/templates/mvc/ts/src/routes/api.ts +76 -0
- package/templates/mvc/ts/src/utils/httpCodes.ts +7 -0
- package/templates/mvc/ts/src/utils/logger.ts +22 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# ==========================================
|
|
2
|
+
# Stage 1: Builder
|
|
3
|
+
# ==========================================
|
|
4
|
+
FROM node:18-alpine AS builder
|
|
5
|
+
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
COPY package*.json ./
|
|
9
|
+
COPY tsconfig*.json ./
|
|
10
|
+
|
|
11
|
+
# Install ALL dependencies (including devDeps for build)
|
|
12
|
+
RUN npm ci
|
|
13
|
+
|
|
14
|
+
COPY . .
|
|
15
|
+
|
|
16
|
+
# Build for production
|
|
17
|
+
<% if (language === 'TypeScript') { %>RUN npm run build<% } %>
|
|
18
|
+
|
|
19
|
+
# ==========================================
|
|
20
|
+
# Stage 2: Production
|
|
21
|
+
# ==========================================
|
|
22
|
+
FROM node:18-alpine AS production
|
|
23
|
+
|
|
24
|
+
WORKDIR /app
|
|
25
|
+
|
|
26
|
+
ENV NODE_ENV=production
|
|
27
|
+
|
|
28
|
+
COPY package*.json ./
|
|
29
|
+
|
|
30
|
+
# Install ONLY production dependencies
|
|
31
|
+
RUN npm ci --only=production --ignore-scripts
|
|
32
|
+
|
|
33
|
+
# Copy built artifacts from builder
|
|
34
|
+
<% if (language === 'TypeScript') { %>
|
|
35
|
+
COPY --from=builder /app/dist ./dist
|
|
36
|
+
<% } else { %>
|
|
37
|
+
COPY --from=builder /app/src ./src
|
|
38
|
+
<% } %>
|
|
39
|
+
|
|
40
|
+
# Copy other necessary files (like views if MVC)
|
|
41
|
+
<% if (viewEngine && viewEngine !== 'None') { %>
|
|
42
|
+
COPY --from=builder /app/src/views ./dist/views
|
|
43
|
+
<% } %>
|
|
44
|
+
|
|
45
|
+
EXPOSE 3000
|
|
46
|
+
|
|
47
|
+
USER node
|
|
48
|
+
|
|
49
|
+
CMD ["npm", "start"]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# <%= projectName %>
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
<% if (language === 'TypeScript') { %><% } else { %><% } %>
|
|
6
|
+
|
|
7
|
+
A production-ready Node.js microservice generated with **<%= architecture %>** and **<%= database %>**.
|
|
8
|
+
This project comes pre-configured with industry-standard tooling for **Code Quality**, **Testing**, and **Security**.
|
|
9
|
+
|
|
10
|
+
## 🚀 Key Features
|
|
11
|
+
|
|
12
|
+
- **Architecture**: <%= architecture %> (<% if (architecture === 'Clean Architecture') { %>Domain, UseCases, Infrastructure<% } else { %>MVC Pattern<% } %>).
|
|
13
|
+
- **Database**: <%= database %> with **Flyway** migrations.
|
|
14
|
+
- **Security**: Helmet, CORS, Rate Limiting, HPP.
|
|
15
|
+
- **Quality**: Eslint, Prettier, Husky, Lint-Staged.
|
|
16
|
+
- **Testing**: Jest (Unit & Integration).
|
|
17
|
+
- **DevOps**: Multi-stage Docker build, CI/CD ready.
|
|
18
|
+
|
|
19
|
+
## 🛠️ Getting Started
|
|
20
|
+
|
|
21
|
+
### 1. Prerequisites
|
|
22
|
+
- Node.js (v18+)
|
|
23
|
+
- Docker & Docker Compose
|
|
24
|
+
|
|
25
|
+
### 2. Quick Start
|
|
26
|
+
```bash
|
|
27
|
+
# Install dependencies
|
|
28
|
+
npm install
|
|
29
|
+
|
|
30
|
+
# Setup Git Hooks (Husky)
|
|
31
|
+
npm run prepare
|
|
32
|
+
|
|
33
|
+
# Start Infrastructure (DB, etc.)
|
|
34
|
+
docker-compose up -d
|
|
35
|
+
|
|
36
|
+
# Run Development Server
|
|
37
|
+
npm run dev
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 3. Development Standards
|
|
41
|
+
Ensure your code meets quality standards before committing:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Run Linter
|
|
45
|
+
npm run lint
|
|
46
|
+
|
|
47
|
+
# Run Tests
|
|
48
|
+
npm test
|
|
49
|
+
|
|
50
|
+
# Format Code
|
|
51
|
+
npm run format
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 📂 Project Structure
|
|
55
|
+
|
|
56
|
+
The project follows **<%= architecture %>** principles.
|
|
57
|
+
<% if (communication === 'Kafka') { -%>
|
|
58
|
+
Microservices communication handled via **Kafka**.
|
|
59
|
+
<% } else { -%>
|
|
60
|
+
API is exposed via **REST**.
|
|
61
|
+
A Swagger UI for API documentation is available at:
|
|
62
|
+
- **URL**: `http://localhost:3000/api-docs` (Dynamic based on PORT)
|
|
63
|
+
<% } -%>
|
|
64
|
+
|
|
65
|
+
## 📝 Logging
|
|
66
|
+
This project uses **Winston** for structured logging.
|
|
67
|
+
- **Development**: Logs are printed to the console.
|
|
68
|
+
- **Production**: Logs are saved to files:
|
|
69
|
+
- `error.log`: Only error level logs.
|
|
70
|
+
- `combined.log`: All logs.
|
|
71
|
+
|
|
72
|
+
## 🐳 Docker Deployment
|
|
73
|
+
This project uses a **Multi-Stage Dockerfile** for optimized production images.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Build Production Image
|
|
77
|
+
docker build -t <%= projectName %> .
|
|
78
|
+
|
|
79
|
+
# Run Container
|
|
80
|
+
docker run -p 3000:3000 -e DATABASE_URL=... <%= projectName %>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## 🔒 Security Features
|
|
84
|
+
- **Helmet**: Sets secure HTTP headers.
|
|
85
|
+
- **CORS**: Configured for cross-origin requests.
|
|
86
|
+
- **Rate Limiting**: Protects against DDoS / Brute-force.
|
|
87
|
+
- **HPP**: Prevents HTTP Parameter Pollution attacks.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const { Sequelize } = require('sequelize');
|
|
2
|
+
require('dotenv').config();
|
|
3
|
+
|
|
4
|
+
// Determine dialect
|
|
5
|
+
<% if (database === 'MySQL') { %>const dialect = 'mysql';<% } -%>
|
|
6
|
+
<% if (database === 'PostgreSQL') { %>const dialect = 'postgres';<% } -%>
|
|
7
|
+
|
|
8
|
+
const sequelize = new Sequelize(
|
|
9
|
+
process.env.DB_NAME || '<%= dbName %>',
|
|
10
|
+
process.env.DB_USER || '<% if (database === 'MySQL') { %>root<% } else { %>postgres<% } %>',
|
|
11
|
+
process.env.DB_PASSWORD || '<% if (database === 'MySQL') { %>root<% } else { %>postgres<% } %>',
|
|
12
|
+
{
|
|
13
|
+
host: process.env.DB_HOST || '127.0.0.1',
|
|
14
|
+
dialect: dialect,
|
|
15
|
+
logging: false,
|
|
16
|
+
port: parseInt(process.env.DB_PORT || '<% if (database === 'MySQL') { %>3306<% } else { %>5432<% } %>')
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
module.exports = sequelize;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const { DataTypes, Model } = require('sequelize');
|
|
2
|
+
<% if (architecture === 'MVC') { %>const sequelize = require('../config/database');<% } else { %>const sequelize = require('../database');<% } %>
|
|
3
|
+
|
|
4
|
+
class User extends Model {}
|
|
5
|
+
|
|
6
|
+
User.init(
|
|
7
|
+
{
|
|
8
|
+
id: {
|
|
9
|
+
type: DataTypes.INTEGER,
|
|
10
|
+
autoIncrement: true,
|
|
11
|
+
primaryKey: true,
|
|
12
|
+
},
|
|
13
|
+
name: {
|
|
14
|
+
type: DataTypes.STRING,
|
|
15
|
+
allowNull: false,
|
|
16
|
+
},
|
|
17
|
+
email: {
|
|
18
|
+
type: DataTypes.STRING,
|
|
19
|
+
allowNull: false,
|
|
20
|
+
unique: true,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
sequelize,
|
|
25
|
+
tableName: 'users',
|
|
26
|
+
underscored: true,
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
module.exports = User;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Sequelize } from 'sequelize';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
|
|
4
|
+
dotenv.config();
|
|
5
|
+
|
|
6
|
+
// Determine dialect
|
|
7
|
+
<% if (database === 'MySQL') { %>const dialect = 'mysql';<% } -%>
|
|
8
|
+
<% if (database === 'PostgreSQL') { %>const dialect = 'postgres';<% } -%>
|
|
9
|
+
|
|
10
|
+
const sequelize = new Sequelize(
|
|
11
|
+
process.env.DB_NAME || '<%= dbName %>',
|
|
12
|
+
process.env.DB_USER || '<% if (database === 'MySQL') { %>root<% } else { %>postgres<% } %>',
|
|
13
|
+
process.env.DB_PASSWORD || '<% if (database === 'MySQL') { %>root<% } else { %>postgres<% } %>',
|
|
14
|
+
{
|
|
15
|
+
host: process.env.DB_HOST || '127.0.0.1',
|
|
16
|
+
dialect: dialect,
|
|
17
|
+
logging: false,
|
|
18
|
+
port: parseInt(process.env.DB_PORT || '<% if (database === 'MySQL') { %>3306<% } else { %>5432<% } %>')
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export default sequelize;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { DataTypes, Model } from 'sequelize';
|
|
2
|
+
<% if (architecture === 'MVC') { %>import sequelize from '../config/database';<% } else { %>import sequelize from '../database';<% } %>
|
|
3
|
+
|
|
4
|
+
class User extends Model {
|
|
5
|
+
public id!: number;
|
|
6
|
+
public name!: string;
|
|
7
|
+
public email!: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
User.init(
|
|
11
|
+
{
|
|
12
|
+
id: {
|
|
13
|
+
type: DataTypes.INTEGER,
|
|
14
|
+
autoIncrement: true,
|
|
15
|
+
primaryKey: true,
|
|
16
|
+
},
|
|
17
|
+
name: {
|
|
18
|
+
type: DataTypes.STRING,
|
|
19
|
+
allowNull: false,
|
|
20
|
+
},
|
|
21
|
+
email: {
|
|
22
|
+
type: DataTypes.STRING,
|
|
23
|
+
allowNull: false,
|
|
24
|
+
unique: true,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
sequelize,
|
|
29
|
+
tableName: 'users',
|
|
30
|
+
underscored: true,
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
export default User;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
version: '3.8'
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
app:
|
|
5
|
+
build: .
|
|
6
|
+
ports:
|
|
7
|
+
- "${PORT:-3000}:3000"
|
|
8
|
+
depends_on:
|
|
9
|
+
- db
|
|
10
|
+
<% if (communication === 'Kafka') { %> - kafka
|
|
11
|
+
<% } -%>
|
|
12
|
+
<% if (communication === 'Kafka') { %> environment:
|
|
13
|
+
- KAFKA_BROKER=kafka:29092
|
|
14
|
+
- KAFKAJS_NO_PARTITIONER_WARNING=1
|
|
15
|
+
- PORT=3000
|
|
16
|
+
- DB_HOST=db
|
|
17
|
+
<% if (database === 'MySQL') { %> - DB_USER=root
|
|
18
|
+
- DB_PASSWORD=root
|
|
19
|
+
- DB_NAME=<%= dbName %>
|
|
20
|
+
<% } %><% if (database === 'PostgreSQL') { %> - DB_USER=postgres
|
|
21
|
+
- DB_PASSWORD=postgres
|
|
22
|
+
- DB_NAME=<%= dbName %>
|
|
23
|
+
<% } -%>
|
|
24
|
+
<% } else { %>
|
|
25
|
+
environment:
|
|
26
|
+
- PORT=3000
|
|
27
|
+
- DB_HOST=db
|
|
28
|
+
<% if (database === 'MySQL') { %> - DB_USER=root
|
|
29
|
+
- DB_PASSWORD=root
|
|
30
|
+
- DB_NAME=<%= dbName %>
|
|
31
|
+
<% } %><% if (database === 'PostgreSQL') { %> - DB_USER=postgres
|
|
32
|
+
- DB_PASSWORD=postgres
|
|
33
|
+
- DB_NAME=<%= dbName %>
|
|
34
|
+
<% } -%>
|
|
35
|
+
<% } %>
|
|
36
|
+
db:
|
|
37
|
+
<% if (database === 'MySQL') { %> image: mysql:8.0
|
|
38
|
+
command: --default-authentication-plugin=mysql_native_password
|
|
39
|
+
restart: always
|
|
40
|
+
environment:
|
|
41
|
+
MYSQL_ROOT_PASSWORD: root
|
|
42
|
+
MYSQL_DATABASE: <%= dbName %>
|
|
43
|
+
ports:
|
|
44
|
+
- "${DB_PORT:-3306}:3306"
|
|
45
|
+
<% } %><% if (database === 'PostgreSQL') { %> image: postgres:15
|
|
46
|
+
restart: always
|
|
47
|
+
environment:
|
|
48
|
+
POSTGRES_USER: postgres
|
|
49
|
+
POSTGRES_PASSWORD: postgres
|
|
50
|
+
POSTGRES_DB: <%= dbName %>
|
|
51
|
+
ports:
|
|
52
|
+
- "${DB_PORT:-5432}:5432"
|
|
53
|
+
<% } %> volumes:
|
|
54
|
+
- <%= database.toLowerCase() %>_data:/var/lib/<% if (database === 'MySQL') { %>mysql<% } else { %>postgresql/data<% } %>
|
|
55
|
+
|
|
56
|
+
flyway:
|
|
57
|
+
image: flyway/flyway
|
|
58
|
+
command: -connectRetries=60 migrate
|
|
59
|
+
volumes:
|
|
60
|
+
- ./flyway/sql:/flyway/sql
|
|
61
|
+
environment:
|
|
62
|
+
<% if (database === 'MySQL') { %> FLYWAY_URL: jdbc:mysql://db:3306/<%= dbName %>
|
|
63
|
+
FLYWAY_USER: root
|
|
64
|
+
FLYWAY_PASSWORD: root
|
|
65
|
+
<% } %><% if (database === 'PostgreSQL') { %> FLYWAY_URL: jdbc:postgresql://db:5432/<%= dbName %>
|
|
66
|
+
FLYWAY_USER: postgres
|
|
67
|
+
FLYWAY_PASSWORD: postgres
|
|
68
|
+
<% } %> depends_on:
|
|
69
|
+
- db
|
|
70
|
+
<% if (communication === 'Kafka') { %> zookeeper:
|
|
71
|
+
image: confluentinc/cp-zookeeper:7.4.0
|
|
72
|
+
environment:
|
|
73
|
+
ZOOKEEPER_CLIENT_PORT: 2181
|
|
74
|
+
ZOOKEEPER_TICK_TIME: 2000
|
|
75
|
+
ports:
|
|
76
|
+
- "${ZOOKEEPER_PORT:-2181}:2181"
|
|
77
|
+
|
|
78
|
+
kafka:
|
|
79
|
+
image: confluentinc/cp-kafka:7.4.0
|
|
80
|
+
depends_on:
|
|
81
|
+
- zookeeper
|
|
82
|
+
ports:
|
|
83
|
+
- "${KAFKA_PORT:-9092}:9092"
|
|
84
|
+
environment:
|
|
85
|
+
KAFKA_BROKER_ID: 1
|
|
86
|
+
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
|
|
87
|
+
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
|
|
88
|
+
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
|
|
89
|
+
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
|
|
90
|
+
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
|
|
91
|
+
<% } -%>
|
|
92
|
+
volumes:
|
|
93
|
+
<%= database.toLowerCase() %>_data:
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
testEnvironment: 'node',
|
|
3
|
+
coverageDirectory: 'coverage',
|
|
4
|
+
collectCoverageFrom: ['src/**/*.{js,ts}'],
|
|
5
|
+
testMatch: ['**/*.test.ts', '**/*.test.js'],
|
|
6
|
+
<% if (language === 'TypeScript') { %>preset: 'ts-jest',<% } %>
|
|
7
|
+
coveragePathIgnorePatterns: [
|
|
8
|
+
"/node_modules/",
|
|
9
|
+
"/dist/"
|
|
10
|
+
]
|
|
11
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const { kafka } = require('../config/kafka');
|
|
2
|
+
|
|
3
|
+
const producer = kafka.producer();
|
|
4
|
+
const consumer = kafka.consumer({ groupId: 'test-group' });
|
|
5
|
+
|
|
6
|
+
const connectKafka = async () => {
|
|
7
|
+
await producer.connect();
|
|
8
|
+
await consumer.connect();
|
|
9
|
+
await consumer.subscribe({ topic: 'test-topic', fromBeginning: true });
|
|
10
|
+
|
|
11
|
+
await consumer.run({
|
|
12
|
+
eachMessage: async ({ topic, partition, message }) => {
|
|
13
|
+
console.log({
|
|
14
|
+
value: message.value.toString(),
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const sendMessage = async (topic, message) => {
|
|
21
|
+
await producer.send({
|
|
22
|
+
topic,
|
|
23
|
+
messages: [
|
|
24
|
+
{ value: message },
|
|
25
|
+
],
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
module.exports = { connectKafka, sendMessage };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { kafka } from '../config/kafka';
|
|
2
|
+
import { EachMessagePayload, Producer, Consumer } from 'kafkajs';
|
|
3
|
+
|
|
4
|
+
export class KafkaService {
|
|
5
|
+
private producer: Producer;
|
|
6
|
+
private consumer: Consumer;
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
this.producer = kafka.producer();
|
|
10
|
+
this.consumer = kafka.consumer({ groupId: 'test-group' });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async connect() {
|
|
14
|
+
await this.producer.connect();
|
|
15
|
+
await this.consumer.connect();
|
|
16
|
+
await this.consumer.subscribe({ topic: 'test-topic', fromBeginning: true });
|
|
17
|
+
|
|
18
|
+
await this.consumer.run({
|
|
19
|
+
eachMessage: async ({ topic, partition, message }: EachMessagePayload) => {
|
|
20
|
+
console.log({
|
|
21
|
+
value: message.value?.toString(),
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async sendMessage(topic: string, message: string) {
|
|
28
|
+
await this.producer.send({
|
|
29
|
+
topic,
|
|
30
|
+
messages: [
|
|
31
|
+
{ value: message },
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= projectName %>",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Generated by nodejs-quickstart",
|
|
5
|
+
"main": "<% if (language === 'TypeScript') { %>dist/index.js<% } else { %>src/index.js<% } %>",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "<% if (language === 'TypeScript') { %>node dist/index.js<% } else { %>node src/index.js<% } %>",
|
|
8
|
+
"dev": "<% if (language === 'TypeScript') { %>nodemon --exec ts-node src/index.ts<% } else { %>nodemon src/index.js<% } %>"<% if (language === 'TypeScript') { %>,
|
|
9
|
+
"build": "rimraf dist && tsc<% if (viewEngine && viewEngine !== 'None') { %> && copyfiles -u 1 \"src/views/**/*\" dist/<% } %>"<% } %>,
|
|
10
|
+
"lint": "eslint . --ext .ts,.js",
|
|
11
|
+
"lint:fix": "eslint . --ext .ts,.js --fix",
|
|
12
|
+
"format": "prettier --write .",
|
|
13
|
+
"prepare": "husky install",
|
|
14
|
+
"test": "jest",
|
|
15
|
+
"test:watch": "jest --watch",
|
|
16
|
+
"test:coverage": "jest --coverage"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"express": "^4.18.2",
|
|
20
|
+
"dotenv": "^16.3.1",
|
|
21
|
+
<% if (database === 'MySQL') { %> "mysql2": "^3.6.5",
|
|
22
|
+
<% } -%>
|
|
23
|
+
<% if (database === 'PostgreSQL') { %> "pg": "^8.11.3",
|
|
24
|
+
<% } -%>
|
|
25
|
+
"sequelize": "^6.35.2",
|
|
26
|
+
<% if (communication === 'Kafka') { %> "kafkajs": "^2.2.4",
|
|
27
|
+
<% } -%>
|
|
28
|
+
<% if (viewEngine === 'EJS') { %> "ejs": "^3.1.9",
|
|
29
|
+
<% } -%>
|
|
30
|
+
<% if (viewEngine === 'Pug') { %> "pug": "^3.0.2",
|
|
31
|
+
<% } -%>
|
|
32
|
+
"cors": "^2.8.5",
|
|
33
|
+
"helmet": "^7.1.0",
|
|
34
|
+
"hpp": "^0.2.3",
|
|
35
|
+
"express-rate-limit": "^7.1.5",
|
|
36
|
+
"winston": "^3.11.0"<% if (communication === 'REST APIs') { %>,
|
|
37
|
+
"swagger-ui-express": "^5.0.0",
|
|
38
|
+
"swagger-jsdoc": "^6.2.8"
|
|
39
|
+
<% } %>
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"nodemon": "^3.0.2"<% if (language === 'TypeScript') { %>,
|
|
43
|
+
"typescript": "^5.3.3",
|
|
44
|
+
"ts-node": "^10.9.2",
|
|
45
|
+
"@types/node": "^20.10.5",
|
|
46
|
+
"@types/express": "^4.17.21",
|
|
47
|
+
"@types/cors": "^2.8.17",
|
|
48
|
+
"@types/dotenv": "^8.2.0",
|
|
49
|
+
"@types/hpp": "^0.2.3",
|
|
50
|
+
<% if (database === 'PostgreSQL') { %> "@types/pg": "^8.10.9",
|
|
51
|
+
<% } -%>
|
|
52
|
+
"@types/sequelize": "^4.28.19",
|
|
53
|
+
"rimraf": "^5.0.5"<% if (viewEngine && viewEngine !== 'None') { %>,
|
|
54
|
+
"copyfiles": "^2.4.1"<% } %><% } %>,
|
|
55
|
+
"eslint": "^8.56.0",
|
|
56
|
+
"prettier": "^3.1.1",
|
|
57
|
+
"eslint-config-prettier": "^9.1.0",
|
|
58
|
+
"husky": "^8.0.3",
|
|
59
|
+
"lint-staged": "^15.2.0"<% if (language === 'TypeScript') { %>,
|
|
60
|
+
"@typescript-eslint/eslint-plugin": "^6.18.1",
|
|
61
|
+
"@typescript-eslint/parser": "^6.18.1",
|
|
62
|
+
<% if (communication === 'REST APIs') { %> "@types/swagger-ui-express": "^4.1.6",
|
|
63
|
+
"@types/swagger-jsdoc": "^6.0.4",<% } -%>
|
|
64
|
+
"jest": "^29.7.0",
|
|
65
|
+
"ts-jest": "^29.1.1",
|
|
66
|
+
"@types/jest": "^29.5.11",
|
|
67
|
+
"supertest": "^6.3.3",
|
|
68
|
+
"@types/supertest": "^6.0.2"<% } else { %>,
|
|
69
|
+
"jest": "^29.7.0",
|
|
70
|
+
"supertest": "^6.3.3"<% } %>
|
|
71
|
+
},
|
|
72
|
+
"lint-staged": {
|
|
73
|
+
"*.{js,ts}": [
|
|
74
|
+
"eslint --fix",
|
|
75
|
+
"prettier --write"
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"outDir": "./dist",
|
|
6
|
+
"rootDir": "./src",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true
|
|
11
|
+
},
|
|
12
|
+
"include": [
|
|
13
|
+
"src/**/*"
|
|
14
|
+
],
|
|
15
|
+
"exclude": [
|
|
16
|
+
"node_modules",
|
|
17
|
+
"**/*.test.ts"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
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><%= projectName %></title>
|
|
7
|
+
<style>
|
|
8
|
+
body { font-family: sans-serif; padding: 20px; }
|
|
9
|
+
h1 { color: #333; }
|
|
10
|
+
.status { margin-top: 20px; padding: 10px; background: #e0f7fa; border-left: 5px solid #00acc1; }
|
|
11
|
+
</style>
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<h1>Welcome to <%= projectName %></h1>
|
|
15
|
+
<p>Architecture: <strong><%= architecture %></strong></p>
|
|
16
|
+
<p>Database: <strong><%= database %></strong></p>
|
|
17
|
+
|
|
18
|
+
<% if (communication === 'Kafka') { %>
|
|
19
|
+
<div class="status">
|
|
20
|
+
<h3>Kafka Status</h3>
|
|
21
|
+
<p>Connected to Kafka Broker.</p>
|
|
22
|
+
<p>Check console for messages.</p>
|
|
23
|
+
</div>
|
|
24
|
+
<% } else { %>
|
|
25
|
+
<div class="status">
|
|
26
|
+
<h3>API Status</h3>
|
|
27
|
+
<p>REST API is active.</p>
|
|
28
|
+
</div>
|
|
29
|
+
<% } %>
|
|
30
|
+
</body>
|
|
31
|
+
</html>
|
|
@@ -0,0 +1,26 @@
|
|
|
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= projectName
|
|
7
|
+
style.
|
|
8
|
+
body { font-family: sans-serif; padding: 20px; }
|
|
9
|
+
h1 { color: #333; }
|
|
10
|
+
.status { margin-top: 20px; padding: 10px; background: #e0f7fa; border-left: 5px solid #00acc1; }
|
|
11
|
+
body
|
|
12
|
+
h1 Welcome to #{projectName}
|
|
13
|
+
p Architecture:
|
|
14
|
+
strong #{architecture}
|
|
15
|
+
p Database:
|
|
16
|
+
strong #{database}
|
|
17
|
+
|
|
18
|
+
if communication === 'Kafka'
|
|
19
|
+
.status
|
|
20
|
+
h3 Kafka Status
|
|
21
|
+
p Connected to Kafka Broker.
|
|
22
|
+
p Check console for messages.
|
|
23
|
+
else
|
|
24
|
+
.status
|
|
25
|
+
h3 API Status
|
|
26
|
+
p REST API is active.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
CREATE TABLE users (
|
|
2
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
3
|
+
name VARCHAR(255) NOT NULL,
|
|
4
|
+
email VARCHAR(255) NOT NULL UNIQUE,
|
|
5
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
6
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
INSERT INTO users (name, email) VALUES ('Admin User', 'admin@example.com');
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
CREATE TABLE users (
|
|
2
|
+
id SERIAL PRIMARY KEY,
|
|
3
|
+
name VARCHAR(255) NOT NULL,
|
|
4
|
+
email VARCHAR(255) NOT NULL UNIQUE,
|
|
5
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
6
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
INSERT INTO users (name, email) VALUES ('Admin User', 'admin@example.com');
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Database configuration placeholder
|
|
2
|
+
// Use environment variables from .env depending on your DB choice (MySQL/Postgres)
|
|
3
|
+
require('dotenv').config();
|
|
4
|
+
|
|
5
|
+
const dbConfig = {
|
|
6
|
+
host: process.env.DB_HOST,
|
|
7
|
+
user: process.env.DB_USER,
|
|
8
|
+
password: process.env.DB_PASSWORD,
|
|
9
|
+
database: process.env.DB_NAME
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
module.exports = dbConfig;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const swaggerJsdoc = require('swagger-jsdoc');
|
|
2
|
+
|
|
3
|
+
const options = {
|
|
4
|
+
definition: {
|
|
5
|
+
openapi: '3.0.0',
|
|
6
|
+
info: {
|
|
7
|
+
title: 'NodeJS API Service',
|
|
8
|
+
version: '1.0.0',
|
|
9
|
+
description: 'API documentation for the NodeJS Service',
|
|
10
|
+
},
|
|
11
|
+
servers: [
|
|
12
|
+
{
|
|
13
|
+
url: process.env.SERVER_URL || `http://localhost:${process.env.PORT || 3000}`,
|
|
14
|
+
description: 'Server',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
apis: ['./src/routes/*.js'], // Path to the API docs
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const specs = swaggerJsdoc(options);
|
|
22
|
+
|
|
23
|
+
module.exports = specs;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
exports.getUsers = (req, res) => {
|
|
2
|
+
res.json([
|
|
3
|
+
{ id: 1, name: 'John Doe' },
|
|
4
|
+
{ id: 2, name: 'Jane Doe' }
|
|
5
|
+
]);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
exports.createUser = (req, res) => {
|
|
9
|
+
const { name, email } = req.body;
|
|
10
|
+
res.status(201).json({
|
|
11
|
+
message: 'User created successfully',
|
|
12
|
+
user: { name, email }
|
|
13
|
+
});
|
|
14
|
+
};
|