引言
本期周更为大家推荐一个强大的Node.js后端框架——NestJS。NestJS是一个用于构建高效、可扩展的Node.js服务器端应用程序的框架,它使用TypeScript构建,并结合了OOP(面向对象编程)、FP(函数式编程)和FRP(函数响应式编程)的元素。
库介绍
基本信息
- 库名称:NestJS
- GitHub Stars:67,000+
- 维护状态:活跃维护
- 兼容性:Node.js 16+
- 包大小:核心包约 2MB
- 依赖关系:基于Express/Fastify,支持TypeScript
核心特性
- TypeScript优先:完全使用TypeScript编写,提供强类型支持
- 装饰器模式:大量使用装饰器,代码简洁优雅
- 依赖注入:内置强大的依赖注入系统
- 模块化架构:清晰的模块化结构,易于维护
- 多种传输层:支持HTTP、WebSocket、微服务等
- 丰富的生态:集成了大量第三方库和工具
安装使用
全局安装CLI工具
pnpm install -g @nestjs/cli
创建新项目
nest new my-nestjs-app
cd my-nestjs-app
pnpm start:dev
基础项目结构
src/
├── app.controller.ts # 应用控制器
├── app.module.ts # 应用根模块
├── app.service.ts # 应用服务
└── main.ts # 应用入口文件
应用入口文件
// main.ts
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
const bootstrap = async (): Promise<void> => {
const app = await NestFactory.create(AppModule);
// 启用全局验证管道
app.useGlobalPipes(new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}));
// 配置Swagger文档
const config = new DocumentBuilder()
.setTitle('NestJS API')
.setDescription('API文档')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api/docs', app, document);
await app.listen(3000);
};
bootstrap();
核心概念详解
1. 控制器 (Controllers)
控制器负责处理传入的请求并向客户端返回响应。使用装饰器定义路由和HTTP方法:
import { Controller, Get, Post, Body, Param, ParseIntPipe } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
@ApiTags('用户管理')
@Controller('api/users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
@ApiOperation({ summary: '获取所有用户' })
@ApiResponse({ status: 200, description: '获取成功' })
async findAll() {
return await this.usersService.findAll();
}
@Get(':id')
@ApiOperation({ summary: '根据ID获取用户' })
async findOne(@Param('id', ParseIntPipe) id: number) {
return await this.usersService.findOne(id);
}
@Post()
@ApiOperation({ summary: '创建新用户' })
@ApiResponse({ status: 201, description: '创建成功' })
async create(@Body() createUserDto: CreateUserDto) {
return await this.usersService.create(createUserDto);
}
}
2. 服务 (Services)
服务用于处理业务逻辑,通过依赖注入在控制器中使用。使用@Injectable()装饰器标记:
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly usersRepository: Repository<User>,
) {}
async create(createUserDto: CreateUserDto): Promise<User> {
const user = this.usersRepository.create(createUserDto);
return await this.usersRepository.save(user);
}
async findAll(): Promise<User[]> {
return await this.usersRepository.find({
order: { createdAt: 'DESC' },
});
}
async findOne(id: number): Promise<User> {
const user = await this.usersRepository.findOne({ where: { id } });
if (!user) {
throw new NotFoundException(`ID为 ${id} 的用户不存在`);
}
return user;
}
async remove(id: number): Promise<void> {
const user = await this.findOne(id);
await this.usersRepository.remove(user);
}
}
3. 数据传输对象 (DTOs)
DTO用于定义数据结构和验证规则,确保数据的完整性和类型安全:
import { IsString, IsEmail, IsNumber, Min, Max } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class CreateUserDto {
@ApiProperty({ description: '用户名', example: 'john_doe' })
@IsString()
readonly username: string;
@ApiProperty({ description: '邮箱地址', example: 'john@example.com' })
@IsEmail()
readonly email: string;
@ApiProperty({ description: '用户年龄', example: 25 })
@IsNumber()
@Min(1)
@Max(120)
readonly age: number;
}
4. 实体 (Entities)
实体定义数据库表结构,使用TypeORM装饰器:
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true, length: 50 })
username: string;
@Column({ unique: true, length: 100 })
email: string;
@Column({ type: 'int', unsigned: true })
age: number;
@CreateDateColumn()
createdAt: Date;
}
5. 模块 (Modules)
模块是组织应用程序结构的基本单元,将相关组件组合在一起:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { User } from './entities/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService]
})
export class UsersModule {}
6. 中间件 (Middleware)
中间件是在路由处理程序之前调用的函数,用于处理请求和响应:
import { Injectable, NestMiddleware, Logger } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
private readonly logger = new Logger('HTTP');
use(req: Request, res: Response, next: NextFunction): void {
const { method, originalUrl, ip } = req;
const startTime = Date.now();
res.on('finish', () => {
const { statusCode } = res;
const responseTime = Date.now() - startTime;
this.logger.log(`${method} ${originalUrl} ${statusCode} +${responseTime}ms`);
});
next();
}
}
7. 守卫 (Guards)
守卫用于实现认证和授权逻辑:
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const token = request.headers.authorization;
if (!token || !this.validateToken(token)) {
throw new UnauthorizedException('无效的访问令牌');
}
return true;
}
private validateToken(token: string): boolean {
// 实际项目中应该验证JWT token
return token === 'Bearer valid-token';
}
}
8. 拦截器 (Interceptors)
拦截器用于在方法执行前后添加额外逻辑:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class ResponseInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map((data) => ({
success: true,
timestamp: new Date().toISOString(),
data,
})),
);
}
}
9. 异常过滤器 (Exception Filters)
异常过滤器用于处理应用程序中抛出的异常:
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost): void {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message = exception instanceof HttpException
? exception.getResponse()
: '服务器内部错误';
response.status(status).json({
success: false,
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message,
});
}
}
10. 管道 (Pipes)
管道用于数据转换和验证:
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';
@Injectable()
export class ValidationPipe implements PipeTransform<any> {
async transform(value: any, { metatype }: ArgumentMetadata) {
if (!metatype || !this.toValidate(metatype)) {
return value;
}
const object = plainToClass(metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException('数据验证失败');
}
return value;
}
private toValidate(metatype: Function): boolean {
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}
}
11. 自定义装饰器 (Custom Decorators)
创建自定义装饰器来简化代码:
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
// 获取当前用户装饰器
export const CurrentUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
// 使用示例
@Get('profile')
async getProfile(@CurrentUser() user: User) {
return user;
}
12. 配置管理 (Configuration)
使用ConfigModule管理应用配置:
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AppConfigService {
constructor(private configService: ConfigService) {}
get port(): number {
return this.configService.get<number>('PORT', 3000);
}
get databaseUrl(): string {
return this.configService.get<string>('DATABASE_URL');
}
get jwtSecret(): string {
return this.configService.get<string>('JWT_SECRET');
}
}
13. 缓存 (Caching)
内置缓存支持,提高应用性能:
import { Injectable, CACHE_MANAGER, Inject } from '@nestjs/common';
import { Cache } from 'cache-manager';
@Injectable()
export class CacheService {
constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}
async get<T>(key: string): Promise<T | undefined> {
return await this.cacheManager.get<T>(key);
}
async set(key: string, value: any, ttl: number = 300): Promise<void> {
await this.cacheManager.set(key, value, ttl);
}
async del(key: string): Promise<void> {
await this.cacheManager.del(key);
}
}
// 在控制器中使用缓存装饰器
@Get(':id')
@CacheKey('user_profile')
@CacheTTL(300)
async findOne(@Param('id') id: string) {
return this.usersService.findOne(+id);
}
14. 任务调度 (Task Scheduling)
使用@nestjs/schedule进行定时任务:
import { Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
@Injectable()
export class TasksService {
@Cron(CronExpression.EVERY_10_SECONDS)
handleCron() {
console.log('每10秒执行一次的任务');
}
@Cron('0 0 * * *') // 每天午夜执行
async handleDailyTask() {
console.log('执行每日数据清理任务');
// 执行数据清理逻辑
}
}
15. 单元测试 (Unit Testing)
NestJS提供了完整的测试支持:
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
describe('UsersService', () => {
let service: UsersService;
let mockRepository: any;
beforeEach(async () => {
mockRepository = {
find: jest.fn(),
findOne: jest.fn(),
create: jest.fn(),
save: jest.fn(),
remove: jest.fn(),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
UsersService,
{
provide: getRepositoryToken(User),
useValue: mockRepository,
},
],
}).compile();
service = module.get<UsersService>(UsersService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should find all users', async () => {
const users = [{ id: 1, username: 'test' }];
mockRepository.find.mockReturnValue(users);
const result = await service.findAll();
expect(result).toEqual(users);
expect(mockRepository.find).toHaveBeenCalled();
});
});
实际应用场景
1. RESTful API开发
NestJS非常适合构建RESTful API,提供了完整的HTTP方法支持和参数验证。
2. 微服务架构
支持多种传输层(TCP、Redis、NATS等),适合构建微服务系统。
3. GraphQL API
内置GraphQL支持,可以快速构建GraphQL服务。
4. WebSocket应用
支持WebSocket和Socket.io,适合实时应用开发。
5. 企业级应用
完整的架构支持、安全特性和监控能力,适合大型企业应用。
6. 电商平台后端
支持复杂的业务逻辑、用户管理、订单处理等功能。
高级功能特性
WebSocket 实时通信
NestJS提供了完整的WebSocket支持:
import { WebSocketGateway, WebSocketServer, SubscribeMessage, MessageBody } from '@nestjs/websockets';
import { Server } from 'socket.io';
@WebSocketGateway({
cors: {
origin: '*',
},
})
export class ChatGateway {
@WebSocketServer()
server: Server;
@SubscribeMessage('message')
handleMessage(@MessageBody() data: string): void {
this.server.emit('message', data);
}
@SubscribeMessage('join-room')
handleJoinRoom(@MessageBody() room: string): void {
// 加入房间逻辑
this.server.to(room).emit('user-joined', '新用户加入');
}
}
GraphQL 集成
支持GraphQL API开发:
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { UsersService } from './users.service';
import { User } from './entities/user.entity';
import { CreateUserInput } from './dto/create-user.input';
@Resolver(() => User)
export class UsersResolver {
constructor(private readonly usersService: UsersService) {}
@Query(() => [User], { name: 'users' })
findAll() {
return this.usersService.findAll();
}
@Query(() => User, { name: 'user' })
findOne(@Args('id') id: number) {
return this.usersService.findOne(id);
}
@Mutation(() => User)
createUser(@Args('createUserInput') createUserInput: CreateUserInput) {
return this.usersService.create(createUserInput);
}
}
微服务通信
支持多种微服务通信方式:
import { Controller } from '@nestjs/common';
import { MessagePattern, ClientProxy, Client, Transport } from '@nestjs/microservices';
@Controller()
export class MicroserviceController {
@Client({
transport: Transport.TCP,
options: { host: 'localhost', port: 8877 },
})
client: ClientProxy;
@MessagePattern({ cmd: 'sum' })
accumulate(data: number[]): number {
return (data || []).reduce((a, b) => a + b, 0);
}
async callMicroservice() {
const pattern = { cmd: 'sum' };
const payload = [1, 2, 3];
return this.client.send<number>(pattern, payload);
}
}
安全特性
NestJS提供了多种安全功能:
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
import * as bcrypt from 'bcrypt';
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
) {}
async validateUser(username: string, password: string): Promise<any> {
const user = await this.usersService.findByUsername(username);
if (user && await bcrypt.compare(password, user.password)) {
const { password, ...result } = user;
return result;
}
return null;
}
async login(user: any) {
const payload = { username: user.username, sub: user.userId };
return {
access_token: this.jwtService.sign(payload),
refresh_token: this.jwtService.sign(payload, { expiresIn: '7d' }),
};
}
async hashPassword(password: string): Promise<string> {
const saltRounds = 10;
return await bcrypt.hash(password, saltRounds);
}
}
健康检查
监控应用程序健康状态:
import { Controller, Get } from '@nestjs/common';
import { HealthCheckService, HttpHealthIndicator, TypeOrmHealthIndicator, HealthCheck } from '@nestjs/terminus';
@Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private http: HttpHealthIndicator,
private db: TypeOrmHealthIndicator,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.http.pingCheck('nestjs-docs', 'https://docs.nestjs.com'),
() => this.db.pingCheck('database'),
]);
}
}
文件上传处理
使用Multer处理文件上传:
import { Controller, Post, UseInterceptors, UploadedFile, UploadedFiles } from '@nestjs/common';
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { extname } from 'path';
@Controller('upload')
export class UploadController {
// 单文件上传
@Post('single')
@UseInterceptors(
FileInterceptor('file', {
storage: diskStorage({
destination: './uploads',
filename: (req, file, callback) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
callback(null, file.fieldname + '-' + uniqueSuffix + extname(file.originalname));
},
}),
fileFilter: (req, file, callback) => {
if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
return callback(new Error('只允许上传图片文件'), false);
}
callback(null, true);
},
limits: {
fileSize: 5 * 1024 * 1024, // 5MB
},
}),
)
uploadSingle(@UploadedFile() file: Express.Multer.File) {
return {
message: '文件上传成功',
filename: file.filename,
originalname: file.originalname,
size: file.size,
path: file.path,
};
}
// 多文件上传
@Post('multiple')
@UseInterceptors(
FilesInterceptor('files', 10, {
storage: diskStorage({
destination: './uploads',
filename: (req, file, callback) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
callback(null, file.fieldname + '-' + uniqueSuffix + extname(file.originalname));
},
}),
}),
)
uploadMultiple(@UploadedFiles() files: Array<Express.Multer.File>) {
return {
message: '文件上传成功',
files: files.map(file => ({
filename: file.filename,
originalname: file.originalname,
size: file.size,
path: file.path,
})),
};
}
}
邮件发送服务
使用Nodemailer发送邮件:
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as nodemailer from 'nodemailer';
@Injectable()
export class MailService {
private transporter: nodemailer.Transporter;
constructor(private configService: ConfigService) {
this.transporter = nodemailer.createTransporter({
host: this.configService.get('MAIL_HOST'),
port: this.configService.get('MAIL_PORT'),
secure: false, // true for 465, false for other ports
auth: {
user: this.configService.get('MAIL_USER'),
pass: this.configService.get('MAIL_PASS'),
},
});
}
/**
* 发送简单文本邮件
* @param to 收件人邮箱
* @param subject 邮件主题
* @param text 邮件内容
*/
async sendMail(to: string, subject: string, text: string): Promise<void> {
const mailOptions = {
from: this.configService.get('MAIL_FROM'),
to,
subject,
text,
};
await this.transporter.sendMail(mailOptions);
}
/**
* 发送HTML邮件
* @param to 收件人邮箱
* @param subject 邮件主题
* @param html HTML内容
*/
async sendHtmlMail(to: string, subject: string, html: string): Promise<void> {
const mailOptions = {
from: this.configService.get('MAIL_FROM'),
to,
subject,
html,
};
await this.transporter.sendMail(mailOptions);
}
/**
* 发送验证码邮件
* @param to 收件人邮箱
* @param code 验证码
*/
async sendVerificationCode(to: string, code: string): Promise<void> {
const html = `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #333;">邮箱验证</h2>
<p>您的验证码是:</p>
<div style="background: #f5f5f5; padding: 20px; text-align: center; font-size: 24px; font-weight: bold; color: #e74c3c;">
${code}
</div>
<p style="color: #666;">验证码有效期为10分钟,请及时使用。</p>
</div>
`;
await this.sendHtmlMail(to, '邮箱验证码', html);
}
}
// 邮件控制器
@Controller('mail')
export class MailController {
constructor(private readonly mailService: MailService) {}
@Post('send')
async sendMail(@Body() sendMailDto: SendMailDto) {
await this.mailService.sendMail(
sendMailDto.to,
sendMailDto.subject,
sendMailDto.text,
);
return { message: '邮件发送成功' };
}
@Post('verification')
async sendVerification(@Body() { email }: { email: string }) {
const code = Math.random().toString().slice(2, 8);
await this.mailService.sendVerificationCode(email, code);
return { message: '验证码已发送', code }; // 实际项目中不应返回验证码
}
}
日志记录系统
使用Winston进行日志管理:
import { Injectable, LoggerService } from '@nestjs/common';
import * as winston from 'winston';
import * as DailyRotateFile from 'winston-daily-rotate-file';
@Injectable()
export class CustomLogger implements LoggerService {
private logger: winston.Logger;
constructor() {
this.logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}),
winston.format.errors({ stack: true }),
winston.format.json(),
),
defaultMeta: { service: 'nestjs-app' },
transports: [
// 控制台输出
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple(),
),
}),
// 错误日志文件
new DailyRotateFile({
filename: 'logs/error-%DATE%.log',
datePattern: 'YYYY-MM-DD',
level: 'error',
maxSize: '20m',
maxFiles: '14d',
}),
// 所有日志文件
new DailyRotateFile({
filename: 'logs/combined-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '14d',
}),
],
});
}
/**
* 记录普通日志
* @param message 日志消息
* @param context 上下文
*/
log(message: string, context?: string): void {
this.logger.info(message, { context });
}
/**
* 记录错误日志
* @param message 错误消息
* @param trace 错误堆栈
* @param context 上下文
*/
error(message: string, trace?: string, context?: string): void {
this.logger.error(message, { trace, context });
}
/**
* 记录警告日志
* @param message 警告消息
* @param context 上下文
*/
warn(message: string, context?: string): void {
this.logger.warn(message, { context });
}
/**
* 记录调试日志
* @param message 调试消息
* @param context 上下文
*/
debug(message: string, context?: string): void {
this.logger.debug(message, { context });
}
/**
* 记录详细日志
* @param message 详细消息
* @param context 上下文
*/
verbose(message: string, context?: string): void {
this.logger.verbose(message, { context });
}
}
// 日志拦截器
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
constructor(private readonly logger: CustomLogger) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const { method, url, ip } = request;
const userAgent = request.get('User-Agent') || '';
const startTime = Date.now();
this.logger.log(
`${method} ${url} - ${ip} - ${userAgent}`,
'HTTP Request',
);
return next.handle().pipe(
tap(() => {
const response = context.switchToHttp().getResponse();
const { statusCode } = response;
const responseTime = Date.now() - startTime;
this.logger.log(
`${method} ${url} ${statusCode} - ${responseTime}ms`,
'HTTP Response',
);
}),
catchError((error) => {
const responseTime = Date.now() - startTime;
this.logger.error(
`${method} ${url} - ${error.message} - ${responseTime}ms`,
error.stack,
'HTTP Error',
);
throw error;
}),
);
}
}
// 在main.ts中使用自定义日志器
// app.useLogger(app.get(CustomLogger));
国际化支持
使用nestjs-i18n实现多语言支持:
import { Injectable } from '@nestjs/common';
import { I18nService } from 'nestjs-i18n';
// 语言文件结构 (src/i18n/zh/messages.json)
// {
// "user": {
// "welcome": "欢迎 {name}!",
// "notFound": "用户不存在",
// "created": "用户创建成功"
// },
// "validation": {
// "required": "{field} 是必填项",
// "email": "请输入有效的邮箱地址"
// }
// }
// 语言文件结构 (src/i18n/en/messages.json)
// {
// "user": {
// "welcome": "Welcome {name}!",
// "notFound": "User not found",
// "created": "User created successfully"
// },
// "validation": {
// "required": "{field} is required",
// "email": "Please enter a valid email address"
// }
// }
@Injectable()
export class I18nHelper {
constructor(private readonly i18n: I18nService) {}
/**
* 获取翻译文本
* @param key 翻译键
* @param args 参数
* @param lang 语言
*/
translate(key: string, args?: any, lang?: string): string {
return this.i18n.translate(key, { args, lang });
}
/**
* 获取用户相关翻译
* @param key 翻译键
* @param args 参数
* @param lang 语言
*/
translateUser(key: string, args?: any, lang?: string): string {
return this.translate(`user.${key}`, args, lang);
}
/**
* 获取验证相关翻译
* @param key 翻译键
* @param args 参数
* @param lang 语言
*/
translateValidation(key: string, args?: any, lang?: string): string {
return this.translate(`validation.${key}`, args, lang);
}
}
@Controller('i18n-users')
export class I18nUsersController {
constructor(
private readonly usersService: UsersService,
private readonly i18nHelper: I18nHelper,
) {}
/**
* 创建用户
* @param createUserDto 用户数据
* @param lang 语言
*/
@Post()
async create(
@Body() createUserDto: CreateUserDto,
@I18nLang() lang: string,
): Promise<{ message: string; user: any }> {
const user = await this.usersService.create(createUserDto);
return {
message: this.i18nHelper.translateUser('created', {}, lang),
user,
};
}
/**
* 获取用户
* @param id 用户ID
* @param lang 语言
*/
@Get(':id')
async findOne(
@Param('id') id: string,
@I18nLang() lang: string,
): Promise<{ message: string; user?: any }> {
const user = await this.usersService.findOne(+id);
if (!user) {
throw new NotFoundException(
this.i18nHelper.translateUser('notFound', {}, lang),
);
}
return {
message: this.i18nHelper.translateUser('welcome', { name: user.name }, lang),
user,
};
}
}
// 自定义验证装饰器
export function IsEmailI18n(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'isEmailI18n',
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
validator: {
validate(value: any) {
return typeof value === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
},
defaultMessage(args: ValidationArguments) {
return 'validation.email';
},
},
});
};
}
// 在app.module.ts中配置
// I18nModule.forRoot({
// fallbackLanguage: 'en',
// loaderOptions: {
// path: path.join(__dirname, '/i18n/'),
// watch: true,
// },
// resolvers: [
// { use: QueryResolver, options: ['lang'] },
// AcceptLanguageResolver,
// new HeaderResolver(['x-lang']),
// ],
// })
数据库集成
TypeORM集成
首先安装必要的依赖:
pnpm install @nestjs/typeorm typeorm mysql2
配置数据库连接和应用根模块:
// app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { UsersModule } from './users/users.module';
import { User } from './users/entities/user.entity';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
type: 'mysql',
host: configService.get('DB_HOST', 'localhost'),
port: configService.get('DB_PORT', 3306),
username: configService.get('DB_USERNAME', 'root'),
password: configService.get('DB_PASSWORD', 'password'),
database: configService.get('DB_DATABASE', 'nestjs_demo'),
entities: [User],
synchronize: configService.get('NODE_ENV') !== 'production',
logging: configService.get('NODE_ENV') === 'development',
}),
inject: [ConfigService],
}),
UsersModule,
],
})
export class AppModule {}
环境配置文件
创建.env文件来管理环境变量:
# .env
DB_HOST=localhost
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=your_password
DB_DATABASE=nestjs_demo
NODE_ENV=development
数据库迁移
使用TypeORM迁移管理数据库结构变更:
// 创建迁移文件
// pnpm run migration:generate src/migrations/CreateUserTable
// src/migrations/1234567890123-CreateUserTable.ts
import { MigrationInterface, QueryRunner, Table, Index } from 'typeorm';
export class CreateUserTable1234567890123 implements MigrationInterface {
name = 'CreateUserTable1234567890123';
/**
* 执行迁移 - 创建用户表
* @param queryRunner 查询执行器
*/
public async up(queryRunner: QueryRunner): Promise<void> {
// 创建用户表
await queryRunner.createTable(
new Table({
name: 'users',
columns: [
{
name: 'id',
type: 'int',
isPrimary: true,
isGenerated: true,
generationStrategy: 'increment',
},
{
name: 'email',
type: 'varchar',
length: '255',
isUnique: true,
},
{
name: 'name',
type: 'varchar',
length: '100',
},
{
name: 'password',
type: 'varchar',
length: '255',
},
{
name: 'role',
type: 'enum',
enum: ['admin', 'user'],
default: "'user'",
},
{
name: 'isActive',
type: 'boolean',
default: true,
},
{
name: 'createdAt',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
},
{
name: 'updatedAt',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
onUpdate: 'CURRENT_TIMESTAMP',
},
],
}),
true,
);
// 创建邮箱索引
await queryRunner.createIndex(
'users',
new Index({
name: 'IDX_USER_EMAIL',
columnNames: ['email'],
}),
);
// 创建角色索引
await queryRunner.createIndex(
'users',
new Index({
name: 'IDX_USER_ROLE',
columnNames: ['role'],
}),
);
}
/**
* 回滚迁移 - 删除用户表
* @param queryRunner 查询执行器
*/
public async down(queryRunner: QueryRunner): Promise<void> {
// 删除索引
await queryRunner.dropIndex('users', 'IDX_USER_ROLE');
await queryRunner.dropIndex('users', 'IDX_USER_EMAIL');
// 删除表
await queryRunner.dropTable('users');
}
}
// 添加新字段的迁移示例
// src/migrations/1234567890124-AddUserProfile.ts
export class AddUserProfile1234567890124 implements MigrationInterface {
name = 'AddUserProfile1234567890124';
/**
* 添加用户资料字段
* @param queryRunner 查询执行器
*/
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.addColumns('users', [
{
name: 'avatar',
type: 'varchar',
length: '500',
isNullable: true,
},
{
name: 'phone',
type: 'varchar',
length: '20',
isNullable: true,
},
{
name: 'birthday',
type: 'date',
isNullable: true,
},
]);
// 添加手机号索引
await queryRunner.createIndex(
'users',
new Index({
name: 'IDX_USER_PHONE',
columnNames: ['phone'],
}),
);
}
/**
* 回滚用户资料字段
* @param queryRunner 查询执行器
*/
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropIndex('users', 'IDX_USER_PHONE');
await queryRunner.dropColumns('users', ['avatar', 'phone', 'birthday']);
}
}
// TypeORM配置文件 (ormconfig.ts)
export const dataSourceOptions = {
type: 'mysql' as const,
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT) || 3306,
username: process.env.DB_USERNAME || 'root',
password: process.env.DB_PASSWORD || 'password',
database: process.env.DB_DATABASE || 'nestjs_demo',
entities: ['src/**/*.entity{.ts,.js}'],
migrations: ['src/migrations/*{.ts,.js}'],
synchronize: false, // 生产环境必须为false
logging: process.env.NODE_ENV === 'development',
};
// 迁移命令
// 生成迁移: pnpm run migration:generate src/migrations/MigrationName
// 执行迁移: pnpm run migration:run
// 回滚迁移: pnpm run migration:revert
API文档集成
Swagger集成
安装Swagger依赖:
pnpm install @nestjs/swagger swagger-ui-express
在main.ts中配置Swagger:
// main.ts (完整配置)
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
const bootstrap = async (): Promise<void> => {
const app = await NestFactory.create(AppModule);
// 全局验证管道
app.useGlobalPipes(new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}));
// Swagger配置
const config = new DocumentBuilder()
.setTitle('NestJS API')
.setDescription('NestJS示例项目API文档')
.setVersion('1.0')
.addBearerAuth()
.addTag('用户管理', '用户相关接口')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api/docs', app, document);
await app.listen(3000);
console.log('应用启动成功: http://localhost:3000');
console.log('API文档地址: http://localhost:3000/api/docs');
};
bootstrap();
API版本控制
实现API版本管理,支持多版本并存:
// 版本控制策略配置
import { VersioningType } from '@nestjs/common';
// 在main.ts中启用版本控制
const bootstrap = async (): Promise<void> => {
const app = await NestFactory.create(AppModule);
// 启用API版本控制
app.enableVersioning({
type: VersioningType.URI, // URI版本控制: /v1/users
// type: VersioningType.HEADER, // Header版本控制: X-API-Version: 1
// type: VersioningType.MEDIA_TYPE, // Media Type版本控制: Accept: application/json;v=1
defaultVersion: '1',
});
await app.listen(3000);
};
// V1版本用户控制器
@Controller({
path: 'users',
version: '1',
})
@ApiTags('用户管理 V1')
export class UsersV1Controller {
constructor(private readonly usersService: UsersService) {}
/**
* 获取用户列表 (V1)
* @param query 查询参数
*/
@Get()
@Version('1')
@ApiOperation({ summary: '获取用户列表 (V1版本)' })
@ApiResponse({ status: 200, description: '用户列表' })
async findAll(@Query() query: any): Promise<any[]> {
// V1版本的简单实现
return this.usersService.findAll();
}
/**
* 获取用户详情 (V1)
* @param id 用户ID
*/
@Get(':id')
@Version('1')
@ApiOperation({ summary: '获取用户详情 (V1版本)' })
async findOne(@Param('id') id: string): Promise<any> {
const user = await this.usersService.findOne(+id);
// V1版本返回基础字段
return {
id: user.id,
name: user.name,
email: user.email,
};
}
}
// V2版本用户控制器
@Controller({
path: 'users',
version: '2',
})
@ApiTags('用户管理 V2')
export class UsersV2Controller {
constructor(private readonly usersService: UsersService) {}
/**
* 获取用户列表 (V2)
* @param query 查询参数
*/
@Get()
@Version('2')
@ApiOperation({ summary: '获取用户列表 (V2版本)' })
@ApiResponse({ status: 200, description: '用户列表' })
async findAll(
@Query() query: PaginationDto,
): Promise<{ data: any[]; total: number; page: number }> {
// V2版本支持分页
const { page = 1, limit = 10 } = query;
const [data, total] = await this.usersService.findAndCount({
skip: (page - 1) * limit,
take: limit,
});
return {
data,
total,
page: +page,
};
}
/**
* 获取用户详情 (V2)
* @param id 用户ID
*/
@Get(':id')
@Version('2')
@ApiOperation({ summary: '获取用户详情 (V2版本)' })
async findOne(@Param('id') id: string): Promise<any> {
const user = await this.usersService.findOne(+id);
// V2版本返回完整字段
return {
id: user.id,
name: user.name,
email: user.email,
role: user.role,
isActive: user.isActive,
avatar: user.avatar,
phone: user.phone,
createdAt: user.createdAt,
updatedAt: user.updatedAt,
};
}
}
// 版本兼容性装饰器
export const ApiVersions = (...versions: string[]) => {
return applyDecorators(
...versions.map(version => Version(version)),
);
};
// 使用示例
@Controller('products')
export class ProductsController {
/**
* 支持多个版本的接口
*/
@Get()
@ApiVersions('1', '2')
@ApiOperation({ summary: '获取产品列表 (支持V1和V2)' })
async findAll(@Version() version: string): Promise<any> {
if (version === '1') {
// V1版本逻辑
return this.getProductsV1();
} else {
// V2版本逻辑
return this.getProductsV2();
}
}
private getProductsV1(): any {
// V1版本实现
return { version: 'v1', data: [] };
}
private getProductsV2(): any {
// V2版本实现
return { version: 'v2', data: [], meta: {} };
}
}
// 版本弃用警告中间件
@Injectable()
export class VersionDeprecationMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void): void {
const version = req.headers['x-api-version'] ||
req.url.match(/\/v(\d+)\//)?.[1] || '1';
// 检查是否为弃用版本
if (version === '1') {
res.setHeader('X-API-Deprecated', 'true');
res.setHeader('X-API-Sunset', '2024-12-31');
res.setHeader('X-API-Migration-Guide', 'https://api.example.com/migration/v1-to-v2');
}
next();
}
}
// 分页DTO
export class PaginationDto {
@ApiPropertyOptional({ description: '页码', default: 1 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number = 1;
@ApiPropertyOptional({ description: '每页数量', default: 10 })
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
@Max(100)
limit?: number = 10;
}
限流和防护功能
实现API限流、防护和安全措施:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
import { SetMetadata } from '@nestjs/common';
// 限流配置
// 在app.module.ts中配置
// ThrottlerModule.forRoot({
// ttl: 60, // 时间窗口(秒)
// limit: 10, // 限制次数
// })
// 自定义限流装饰器
export const Throttle = (limit: number, ttl: number) =>
SetMetadata('throttle', { limit, ttl });
// 自定义限流守卫
@Injectable()
export class CustomThrottlerGuard extends ThrottlerGuard {
/**
* 获取限流配置
* @param context 执行上下文
*/
protected getTracker(req: Record<string, any>): string {
// 基于IP和用户ID的限流
const userId = req.user?.id;
const ip = req.ip;
return userId ? `user-${userId}` : `ip-${ip}`;
}
/**
* 生成限流错误消息
* @param context 执行上下文
*/
protected generateErrorMessage(): string {
return '请求过于频繁,请稍后再试';
}
}
// IP白名单守卫
@Injectable()
export class IpWhitelistGuard implements CanActivate {
private readonly whitelist = [
'127.0.0.1',
'::1',
'192.168.1.0/24', // 支持CIDR格式
];
/**
* 检查IP是否在白名单中
* @param context 执行上下文
*/
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const clientIp = this.getClientIp(request);
return this.isIpAllowed(clientIp);
}
/**
* 获取客户端真实IP
* @param request 请求对象
*/
private getClientIp(request: any): string {
return (
request.headers['x-forwarded-for']?.split(',')[0] ||
request.headers['x-real-ip'] ||
request.connection.remoteAddress ||
request.socket.remoteAddress ||
request.ip
);
}
/**
* 检查IP是否被允许
* @param ip 客户端IP
*/
private isIpAllowed(ip: string): boolean {
return this.whitelist.some(allowedIp => {
if (allowedIp.includes('/')) {
// CIDR格式检查
return this.isIpInCidr(ip, allowedIp);
}
return ip === allowedIp;
});
}
/**
* 检查IP是否在CIDR范围内
* @param ip 客户端IP
* @param cidr CIDR格式的IP范围
*/
private isIpInCidr(ip: string, cidr: string): boolean {
// 简化的CIDR检查实现
const [network, prefixLength] = cidr.split('/');
// 实际项目中建议使用专门的库如 'ip' 或 'netmask'
return ip.startsWith(network.split('.').slice(0, Math.floor(+prefixLength / 8)).join('.'));
}
}
// 请求大小限制中间件
@Injectable()
export class RequestSizeLimitMiddleware implements NestMiddleware {
private readonly maxSize = 10 * 1024 * 1024; // 10MB
/**
* 检查请求大小
* @param req 请求对象
* @param res 响应对象
* @param next 下一个中间件
*/
use(req: any, res: any, next: () => void): void {
const contentLength = parseInt(req.headers['content-length'] || '0');
if (contentLength > this.maxSize) {
res.status(413).json({
statusCode: 413,
message: '请求体过大',
error: 'Payload Too Large',
});
return;
}
next();
}
}
// 安全头中间件
@Injectable()
export class SecurityHeadersMiddleware implements NestMiddleware {
/**
* 添加安全响应头
* @param req 请求对象
* @param res 响应对象
* @param next 下一个中间件
*/
use(req: any, res: any, next: () => void): void {
// 防止点击劫持
res.setHeader('X-Frame-Options', 'DENY');
// 防止MIME类型嗅探
res.setHeader('X-Content-Type-Options', 'nosniff');
// XSS保护
res.setHeader('X-XSS-Protection', '1; mode=block');
// 强制HTTPS
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
// 内容安全策略
res.setHeader('Content-Security-Policy', "default-src 'self'");
// 隐藏服务器信息
res.removeHeader('X-Powered-By');
next();
}
}
// 使用示例控制器
@Controller('protected')
@UseGuards(CustomThrottlerGuard, IpWhitelistGuard)
export class ProtectedController {
/**
* 高频限流接口
*/
@Get('high-frequency')
@Throttle(100, 60) // 每分钟100次
@ApiOperation({ summary: '高频访问接口' })
async highFrequency(): Promise<{ message: string }> {
return { message: '高频访问成功' };
}
/**
* 低频限流接口
*/
@Post('low-frequency')
@Throttle(5, 60) // 每分钟5次
@ApiOperation({ summary: '低频访问接口' })
async lowFrequency(@Body() data: any): Promise<{ message: string }> {
return { message: '低频访问成功' };
}
/**
* 管理员专用接口
*/
@Get('admin-only')
@UseGuards(IpWhitelistGuard)
@ApiOperation({ summary: '管理员专用接口' })
async adminOnly(): Promise<{ message: string }> {
return { message: '管理员访问成功' };
}
}
// 全局异常过滤器增强
@Catch()
export class SecurityExceptionFilter implements ExceptionFilter {
/**
* 处理安全相关异常
* @param exception 异常对象
* @param host 参数主机
*/
catch(exception: any, host: ArgumentsHost): void {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
let status = 500;
let message = '服务器内部错误';
if (exception instanceof ThrottlerException) {
status = 429;
message = '请求过于频繁,请稍后再试';
} else if (exception instanceof UnauthorizedException) {
status = 401;
message = '未授权访问';
} else if (exception instanceof ForbiddenException) {
status = 403;
message = '访问被拒绝';
}
// 记录安全事件
console.log(`Security Event: ${status} - ${request.ip} - ${request.url}`);
response.status(status).json({
statusCode: status,
message,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
// 在main.ts中应用中间件
// app.use(new RequestSizeLimitMiddleware().use);
// app.use(new SecurityHeadersMiddleware().use);
// app.useGlobalFilters(new SecurityExceptionFilter());
事件系统
使用EventEmitter实现应用内事件通信:
import { Injectable } from '@nestjs/common';
import { EventEmitter2, OnEvent } from '@nestjs/event-emitter';
import { Cron, CronExpression } from '@nestjs/schedule';
// 事件数据接口
export interface UserCreatedEvent {
userId: number;
email: string;
name: string;
timestamp: Date;
}
export interface OrderCompletedEvent {
orderId: number;
userId: number;
amount: number;
products: string[];
timestamp: Date;
}
export interface SystemMaintenanceEvent {
type: 'start' | 'end';
message: string;
timestamp: Date;
}
// 事件发布服务
@Injectable()
export class EventPublisherService {
constructor(private readonly eventEmitter: EventEmitter2) {}
/**
* 发布用户创建事件
* @param userData 用户数据
*/
publishUserCreated(userData: Omit<UserCreatedEvent, 'timestamp'>): void {
const event: UserCreatedEvent = {
...userData,
timestamp: new Date(),
};
this.eventEmitter.emit('user.created', event);
}
/**
* 发布订单完成事件
* @param orderData 订单数据
*/
publishOrderCompleted(orderData: Omit<OrderCompletedEvent, 'timestamp'>): void {
const event: OrderCompletedEvent = {
...orderData,
timestamp: new Date(),
};
this.eventEmitter.emit('order.completed', event);
}
/**
* 发布系统维护事件
* @param maintenanceData 维护数据
*/
publishSystemMaintenance(maintenanceData: Omit<SystemMaintenanceEvent, 'timestamp'>): void {
const event: SystemMaintenanceEvent = {
...maintenanceData,
timestamp: new Date(),
};
this.eventEmitter.emit('system.maintenance', event);
}
}
// 邮件通知监听器
@Injectable()
export class EmailNotificationListener {
constructor(private readonly mailService: MailService) {}
/**
* 处理用户创建事件 - 发送欢迎邮件
* @param event 用户创建事件
*/
@OnEvent('user.created')
async handleUserCreated(event: UserCreatedEvent): Promise<void> {
console.log(`发送欢迎邮件给用户: ${event.email}`);
await this.mailService.sendWelcomeEmail({
to: event.email,
name: event.name,
userId: event.userId,
});
}
/**
* 处理订单完成事件 - 发送确认邮件
* @param event 订单完成事件
*/
@OnEvent('order.completed')
async handleOrderCompleted(event: OrderCompletedEvent): Promise<void> {
console.log(`发送订单确认邮件,订单ID: ${event.orderId}`);
await this.mailService.sendOrderConfirmation({
orderId: event.orderId,
amount: event.amount,
products: event.products,
});
}
}
// 数据统计监听器
@Injectable()
export class AnalyticsListener {
constructor(private readonly analyticsService: AnalyticsService) {}
/**
* 处理用户创建事件 - 更新用户统计
* @param event 用户创建事件
*/
@OnEvent('user.created')
async handleUserCreatedAnalytics(event: UserCreatedEvent): Promise<void> {
console.log(`更新用户统计数据`);
await this.analyticsService.incrementUserCount();
await this.analyticsService.trackUserRegistration({
userId: event.userId,
timestamp: event.timestamp,
});
}
/**
* 处理订单完成事件 - 更新销售统计
* @param event 订单完成事件
*/
@OnEvent('order.completed')
async handleOrderCompletedAnalytics(event: OrderCompletedEvent): Promise<void> {
console.log(`更新销售统计数据`);
await this.analyticsService.recordSale({
amount: event.amount,
userId: event.userId,
timestamp: event.timestamp,
});
}
}
// 系统监控监听器
@Injectable()
export class SystemMonitorListener {
constructor(private readonly logger: CustomLogger) {}
/**
* 处理系统维护事件
* @param event 系统维护事件
*/
@OnEvent('system.maintenance')
handleSystemMaintenance(event: SystemMaintenanceEvent): void {
this.logger.log(
`系统维护${event.type === 'start' ? '开始' : '结束'}: ${event.message}`,
'SystemMaintenance',
);
// 可以在这里添加更多监控逻辑
if (event.type === 'start') {
this.enableMaintenanceMode();
} else {
this.disableMaintenanceMode();
}
}
/**
* 启用维护模式
*/
private enableMaintenanceMode(): void {
// 实现维护模式逻辑
console.log('启用维护模式');
}
/**
* 禁用维护模式
*/
private disableMaintenanceMode(): void {
// 实现正常模式逻辑
console.log('禁用维护模式');
}
}
// 定时任务监听器
@Injectable()
export class ScheduledTasksListener {
constructor(
private readonly eventPublisher: EventPublisherService,
private readonly logger: CustomLogger,
) {}
/**
* 每日系统检查
*/
@Cron(CronExpression.EVERY_DAY_AT_2AM)
async dailySystemCheck(): Promise<void> {
this.logger.log('开始每日系统检查', 'ScheduledTasks');
// 发布系统维护开始事件
this.eventPublisher.publishSystemMaintenance({
type: 'start',
message: '每日系统检查开始',
});
// 执行系统检查逻辑
await this.performSystemCheck();
// 发布系统维护结束事件
this.eventPublisher.publishSystemMaintenance({
type: 'end',
message: '每日系统检查完成',
});
}
/**
* 执行系统检查
*/
private async performSystemCheck(): Promise<void> {
// 模拟系统检查
await new Promise(resolve => setTimeout(resolve, 5000));
this.logger.log('系统检查完成', 'ScheduledTasks');
}
}
// 使用事件的控制器示例
@Controller('events')
export class EventsController {
constructor(private readonly eventPublisher: EventPublisherService) {}
/**
* 模拟用户注册
* @param userData 用户数据
*/
@Post('simulate-user-registration')
@ApiOperation({ summary: '模拟用户注册事件' })
async simulateUserRegistration(
@Body() userData: { email: string; name: string },
): Promise<{ message: string }> {
const userId = Math.floor(Math.random() * 1000) + 1;
// 发布用户创建事件
this.eventPublisher.publishUserCreated({
userId,
email: userData.email,
name: userData.name,
});
return { message: '用户注册事件已发布' };
}
/**
* 模拟订单完成
* @param orderData 订单数据
*/
@Post('simulate-order-completion')
@ApiOperation({ summary: '模拟订单完成事件' })
async simulateOrderCompletion(
@Body() orderData: { userId: number; amount: number; products: string[] },
): Promise<{ message: string }> {
const orderId = Math.floor(Math.random() * 10000) + 1;
// 发布订单完成事件
this.eventPublisher.publishOrderCompleted({
orderId,
...orderData,
});
return { message: '订单完成事件已发布' };
}
}
// 在app.module.ts中配置事件模块
// EventEmitterModule.forRoot({
// wildcard: false,
// delimiter: '.',
// newListener: false,
// removeListener: false,
// maxListeners: 10,
// verboseMemoryLeak: false,
// ignoreErrors: false,
// })
优缺点分析
优势
- TypeScript原生支持:提供完整的类型安全
- 架构清晰:模块化设计,代码组织良好
- 装饰器语法:代码简洁,易于理解
- 丰富的功能:内置验证、序列化、缓存等功能
- 活跃社区:文档完善,生态丰富
- 企业级特性:支持微服务、测试、监控等
局限性
- 学习曲线:对于初学者来说概念较多
- 包体积:相比Express等轻量框架较大
- 装饰器依赖:需要启用实验性装饰器特性
- 过度工程:对于简单项目可能过于复杂
性能优化建议
- 使用Fastify:替换默认的Express适配器
- 启用缓存:使用内置缓存机制
- 数据库连接池:合理配置数据库连接
- 异步处理:充分利用Node.js异步特性
完整项目配置
package.json
{
"name": "nestjs-demo",
"version": "1.0.0",
"description": "NestJS 完整功能示例项目",
"scripts": {
"build": "nest build",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"migration:generate": "typeorm-ts-node-commonjs migration:generate",
"migration:run": "typeorm-ts-node-commonjs migration:run",
"migration:revert": "typeorm-ts-node-commonjs migration:revert"
},
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/platform-socket.io": "^10.0.0",
"@nestjs/websockets": "^10.0.0",
"@nestjs/typeorm": "^10.0.0",
"@nestjs/config": "^3.0.0",
"@nestjs/swagger": "^7.0.0",
"@nestjs/jwt": "^10.0.0",
"@nestjs/passport": "^10.0.0",
"@nestjs/schedule": "^4.0.0",
"@nestjs/cache-manager": "^2.0.0",
"@nestjs/graphql": "^12.0.0",
"@nestjs/apollo": "^12.0.0",
"@nestjs/microservices": "^10.0.0",
"@nestjs/terminus": "^10.0.0",
"typeorm": "^0.3.17",
"mysql2": "^3.6.0",
"class-validator": "^0.14.0",
"class-transformer": "^0.5.1",
"swagger-ui-express": "^5.0.0",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"bcrypt": "^5.1.0",
"cache-manager": "^5.2.0",
"apollo-server-express": "^3.12.0",
"graphql": "^16.8.0",
"socket.io": "^4.7.0",
"rxjs": "^7.8.0",
"reflect-metadata": "^0.1.13"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/node": "^20.0.0",
"@types/passport-jwt": "^3.0.9",
"@types/passport-local": "^1.0.35",
"@types/bcrypt": "^5.0.0",
"@types/cache-manager": "^4.0.2",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"eslint": "^8.42.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.5.0",
"prettier": "^3.0.0",
"source-map-support": "^0.5.21",
"supertest": "^6.3.0",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.1.3"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
项目启动步骤
- 创建项目:
nest new my-nestjs-app
cd my-nestjs-app
- 安装依赖:
pnpm install @nestjs/typeorm @nestjs/config @nestjs/swagger typeorm mysql2 class-validator class-transformer
- 配置环境变量:
cp .env.example .env
# 编辑.env文件,配置数据库连接信息
- 启动开发服务器:
pnpm start:dev
总结
NestJS是一个功能强大、架构清晰的Node.js框架,特别适合构建大型、复杂的后端应用。它的TypeScript优先设计、装饰器语法和模块化架构使得代码更加可维护和可扩展。虽然学习曲线相对陡峭,但对于需要构建企业级应用的开发团队来说,NestJS是一个非常值得推荐的选择。
适用场景
- 大型企业级应用开发:完整的架构支持和丰富的功能
- 微服务架构项目:内置微服务支持和多种传输层
- 需要强类型支持的项目:TypeScript原生支持
- 团队协作开发项目:清晰的代码结构和规范
- RESTful API和GraphQL服务:完整的API开发解决方案
推荐理由
- 现代化开发体验:TypeScript + 装饰器 + 依赖注入
- 完整的解决方案:从开发到部署的全套工具和最佳实践
- 可扩展性强:模块化设计,易于扩展和维护
- 社区支持好:活跃的社区和丰富的第三方插件
- 企业级特性:内置安全、验证、缓存、监控等功能
- 学习资源丰富:官方文档详细,示例代码完整
学习建议
- 先掌握TypeScript基础和装饰器语法
- 理解依赖注入和控制反转的概念
- 从简单的CRUD操作开始练习
- 逐步学习高级特性如守卫、拦截器、管道等
- 结合实际项目进行练习
如果你正在寻找一个现代化、功能完整的Node.js后端框架,NestJS绝对值得一试!它不仅能提高开发效率,还能帮助你构建更加健壮和可维护的应用程序。