第 17 章 · NestJS / Gin 框架集成实战
本章与 第 13 章 Spring Boot、第 16 章 Django/Flask/FastAPI 同级,覆盖 Node.js(NestJS) 与 Go(Gin) 在 Kubernetes 内对接全部 PaaS 组件的生产级写法。
| 框架 | 语言 | 典型场景 | 进程 |
|---|---|---|---|
| NestJS | TypeScript | 企业 API、微服务、与前端同构 TS | Node dist/main.js |
| Gin | Go | 高性能网关、云原生微服务、低资源占用 | 单二进制 |
前置:第 2~5 章组件已部署;镜像经第 11 章 Registry 推送。Go 语言基础见 go-dev;Gin 工程化开发见 gin-web ch01~ch15(本章在 K8s 内对接 PaaS 组件,与语言课分工见 gin-web ch17)。
17.1 工程结构对照
nestjs-shop/ gin-shop/
├── src/ ├── cmd/server/
│ ├── main.ts │ └── main.go
│ ├── app.module.ts ├── internal/
│ ├── orders/ │ ├── handler/
│ └── health/ │ ├── service/
├── package.json │ └── config/
├── Dockerfile ├── go.mod
└── nest-cli.json └── Dockerfile
集群 DNS(与第 16 章相同):
mysql.data.svc.cluster.local:3306
redis.data.svc.cluster.local:6379
rabbitmq.middleware.svc.cluster.local:5672
kafka.middleware.svc.cluster.local:9092
rocketmq-namesrv.middleware.svc.cluster.local:9876
elasticsearch.middleware.svc.cluster.local:9200
minio.data.svc.cluster.local:9000
nacos.middleware.svc.cluster.local:8848
keycloak.security.svc.cluster.local:8080
17.2 依赖对照
| 能力 | NestJS 包 | Gin / Go 模块 |
|---|---|---|
| ORM / MySQL | @nestjs/typeorm + mysql2 | gorm.io/gorm + gorm.io/driver/mysql |
| Redis | @nestjs/cache-manager + cache-manager-redis-yet | github.com/redis/go-redis/v9 |
| MongoDB | mongoose / mongodb | go.mongodb.org/mongo-driver |
| 消息队列 | @nestjs/microservices + kafkajs | github.com/IBM/sarama |
| RabbitMQ | @nestjs/bull + bull | github.com/rabbitmq/amqp091-go |
| RocketMQ | rocketmq-client-nodejs | github.com/apache/rocketmq-client-go/v2 |
| ES | @elastic/elasticsearch | github.com/elastic/go-elasticsearch/v8 |
| MinIO | @aws-sdk/client-s3 | github.com/minio/minio-go/v7 |
| 配置 | @nestjs/config | github.com/spf13/viper |
| Nacos | nacos npm SDK | github.com/nacos-group/nacos-sdk-go |
| OIDC | passport + openid-client | github.com/coreos/go-oidc/v3 |
| 限流 | @nestjs/throttler | github.com/ulule/limiter/v3 |
| 指标 | @willsoto/nestjs-prometheus | github.com/prometheus/client_golang |
17.3 NestJS 完整集成
17.3.1 package.json 核心依赖
{
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/config": "^3.2.0",
"@nestjs/typeorm": "^10.0.0",
"@nestjs/cache-manager": "^2.2.0",
"@nestjs/bull": "^10.1.0",
"@nestjs/throttler": "^5.1.0",
"@willsoto/nestjs-prometheus": "^6.0.0",
"typeorm": "^0.3.20",
"mysql2": "^3.9.0",
"cache-manager-redis-yet": "^5.0.0",
"bull": "^4.12.0",
"kafkajs": "^2.2.4",
"rocketmq-client-nodejs": "^1.0.0",
"@elastic/elasticsearch": "^8.14.0",
"@aws-sdk/client-s3": "^3.600.0",
"nacos": "^2.6.0"
}
}
17.3.2 app.module.ts(配置 + 中间件全集)
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CacheModule } from '@nestjs/cache-manager';
import { BullModule } from '@nestjs/bull';
import { ThrottlerModule } from '@nestjs/throttler';
import { PrometheusModule } from '@willsoto/nestjs-prometheus';
import { redisStore } from 'cache-manager-redis-yet';
import { OrdersModule } from './orders/orders.module';
import { HealthController } from './health/health.controller';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
ThrottlerModule.forRoot([{ ttl: 60000, limit: 100 }]),
PrometheusModule.register(),
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (cfg: ConfigService) => ({
type: 'mysql',
host: cfg.get('MYSQL_HOST', 'mysql.data.svc.cluster.local'),
port: +cfg.get('MYSQL_PORT', 3306),
username: cfg.get('MYSQL_USER', 'app'),
password: cfg.get('MYSQL_PASSWORD'),
database: cfg.get('MYSQL_DATABASE', 'appdb'),
autoLoadEntities: true,
synchronize: false,
}),
}),
CacheModule.registerAsync({
isGlobal: true,
inject: [ConfigService],
useFactory: async (cfg: ConfigService) => ({
store: await redisStore({ url: cfg.get('REDIS_URL') }),
}),
}),
BullModule.forRootAsync({
inject: [ConfigService],
useFactory: (cfg: ConfigService) => ({
redis: cfg.get('REDIS_URL'),
// 或 RabbitMQ:createClient URL amqp://...
}),
}),
OrdersModule,
],
controllers: [HealthController],
})
export class AppModule {}
17.3.3 业务服务示例 orders.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
import { InjectQueue } from '@nestjs/bull';
import { Queue } from 'bull';
import { Client, Producer } from 'rocketmq-client-nodejs';
import { Kafka } from 'kafkajs';
import { Client as EsClient } from '@elastic/elasticsearch';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { ConfigService } from '@nestjs/config';
import { Order } from './order.entity';
@Injectable()
export class OrdersService {
private kafka = new Kafka({
brokers: [this.cfg.get('KAFKA_BOOTSTRAP', 'kafka.middleware:9092')],
});
private es = new EsClient({
node: this.cfg.get('ES_URL', 'http://elasticsearch.middleware:9200'),
});
constructor(
@InjectRepository(Order) private repo: Repository<Order>,
@Inject(CACHE_MANAGER) private cache: Cache,
@InjectQueue('notify') private notifyQueue: Queue,
private cfg: ConfigService,
) {}
async create(dto: Partial<Order>) {
const order = await this.repo.save(dto);
await this.cache.set(`order:${order.id}`, order, 3600);
await this.notifyQueue.add('email', { orderId: order.id });
const producer = this.kafka.producer();
await producer.connect();
await producer.send({
topic: 'user-events',
messages: [{ value: JSON.stringify({ event: 'order_created', id: order.id }) }],
});
await producer.disconnect();
return order;
}
async searchProducts(q: string) {
const res = await this.es.search({
index: 'products',
query: { match: { name: q } },
});
return res.hits.hits;
}
async uploadToMinio(key: string, body: Buffer) {
const s3 = new S3Client({
endpoint: this.cfg.get('MINIO_ENDPOINT'),
region: 'us-east-1',
credentials: {
accessKeyId: this.cfg.get('MINIO_ACCESS_KEY')!,
secretAccessKey: this.cfg.get('MINIO_SECRET_KEY')!,
},
forcePathStyle: true,
});
await s3.send(new PutObjectCommand({
Bucket: this.cfg.get('MINIO_BUCKET', 'uploads'),
Key: key,
Body: body,
}));
}
}
17.3.4 main.ts + 健康检查
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT || 3000);
}
bootstrap();
// health.controller.ts
@Controller('health')
export class HealthController {
@Get()
ok() { return { status: 'ok' }; }
}
17.3.5 Dockerfile 与 Deployment
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
EXPOSE 3000