NestJS 完全指南:构建企业级 Node.js 应用
·
文章目录
什么是 NestJS?
NestJS 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的渐进式框架。它使用 TypeScript 构建,并结合了 OOP(面向对象编程)、FP(函数式编程)和 FRP(函数式响应式编程)的元素。
NestJS 在底层使用了 Express(默认)或 Fastify,提供了更高层次的抽象,让开发者能够构建可测试、可扩展、松耦合且易于维护的应用程序。
- NestJS 是一个基于 TypeScript 和 Node.js 的服务器端框架
- 采用模块化架构,深受 Angular 启发
- 内置依赖注入、面向切面编程等企业级特性
- 支持微服务架构、GraphQL、WebSocket 等现代技术
为什么选择 NestJS?
优势与特点
- 开箱即用:提供完整的项目结构和开发工具链
- TypeScript 支持:完整的类型安全,更好的开发体验
- 模块化架构:清晰的代码组织和职责分离
- 依赖注入:松耦合的组件管理和测试友好
- 丰富的生态系统:与各种数据库、消息队列、缓存等无缝集成
- 企业级特性:中间件、守卫、拦截器、管道等
适用场景
- 大型企业级应用
- 微服务架构
- 需要严格类型检查的项目
- 团队协作开发
- 需要高可维护性和可测试性的项目
环境准备与安装
1. 系统要求
- Node.js (版本 16 或更高)
- npm 或 yarn 包管理器
- TypeScript 基础知识
2. 安装 NestJS CLI
# 使用 npm 安装 CLI
npm install -g @nestjs/cli
# 或者使用 yarn
yarn global add @nestjs/cli
# 检查安装是否成功
nest --version
3. 创建新项目
# 创建新项目
nest new my-nest-project
# 进入项目目录
cd my-nest-project
# 启动开发服务器
npm run start:dev
4. 项目结构
my-nest-project/
├── src/
│ ├── app.controller.ts
│ ├── app.controller.spec.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ └── main.ts
├── test/
├── nest-cli.json
├── package.json
├── tsconfig.json
├── tsconfig.build.json
└── README.md
NestJS 核心概念
1. 模块 (Modules)
模块是 NestJS 应用的基本构建块,每个应用至少有一个根模块。
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
@Module({
imports: [UsersModule], // 导入其他模块
controllers: [AppController], // 注册控制器
providers: [AppService], // 注册服务提供者
exports: [AppService], // 导出服务供其他模块使用
})
export class AppModule {}
功能模块示例:
// users/users.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { User } from './user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
2. 控制器 (Controllers)
控制器负责处理传入的请求并返回响应。
// users/users.controller.ts
import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
Query,
HttpStatus,
HttpCode
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
// GET /users
@Get()
async findAll(@Query() query: any) {
return this.usersService.findAll(query);
}
// GET /users/:id
@Get(':id')
async findOne(@Param('id') id: string) {
return this.usersService.findOne(+id);
}
// POST /users
@Post()
@HttpCode(HttpStatus.CREATED)
async create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
// PUT /users/:id
@Put(':id')
async update(
@Param('id') id: string,
@Body() updateUserDto: UpdateUserDto,
) {
return this.usersService.update(+id, updateUserDto);
}
// DELETE /users/:id
@Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT)
async remove(@Param('id') id: string) {
return this.usersService.remove(+id);
}
}
3. 服务 (Services)
服务包含业务逻辑,是可注入的提供者。
// users/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>,
) {}
async findAll(query: any): Promise<User[]> {
return this.usersRepository.find({
where: query,
relations: ['posts'],
});
}
async findOne(id: number): Promise<User> {
const user = await this.usersRepository.findOne({
where: { id },
relations: ['posts'],
});
if (!user) {
throw new NotFoundException(`User with ID ${id} not found`);
}
return user;
}
async create(createUserDto: CreateUserDto): Promise<User> {
const user = this.usersRepository.create(createUserDto);
return this.usersRepository.save(user);
}
async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
const user = await this.findOne(id);
Object.assign(user, updateUserDto);
return this.usersRepository.save(user);
}
async remove(id: number): Promise<void> {
const result = await this.usersRepository.delete(id);
if (result.affected === 0) {
throw new NotFoundException(`User with ID ${id} not found`);
}
}
}
4. 数据传输对象 (DTOs)
DTO 是定义如何通过网络发送数据的对象。
// users/dto/create-user.dto.ts
import {
IsString,
IsEmail,
IsNotEmpty,
MinLength,
MaxLength
} from 'class-validator';
export class CreateUserDto {
@IsString()
@IsNotEmpty()
@MinLength(2)
@MaxLength(50)
username: string;
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
@MinLength(6)
password: string;
}
// users/dto/update-user.dto.ts
import { PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from './create-user.dto';
export class UpdateUserDto extends PartialType(CreateUserDto) {}
数据库集成
1. TypeORM 集成
安装依赖:
npm install @nestjs/typeorm typeorm mysql2
# 或者使用其他数据库驱动
# npm install pg
# npm install sqlite3
配置数据库连接:
// app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'password',
database: 'nest_db',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true, // 生产环境设为 false
logging: true,
}),
UsersModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
2. 实体定义
// users/user.entity.ts
import {
Entity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
OneToMany
} from 'typeorm';
import { Post } from '../posts/post.entity';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
username: string;
@Column({ unique: true })
email: string;
@Column()
password: string;
@Column({ default: true })
isActive: boolean;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
@OneToMany(() => Post, post => post.author)
posts: Post[];
}
中间件与功能组件
1. 中间件 (Middleware)
// middleware/logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const { method, originalUrl } = req;
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${method} ${originalUrl}`);
// 记录响应完成时间
res.on('finish', () => {
const duration = Date.now() - Date.parse(timestamp);
console.log(`[${timestamp}] ${method} ${originalUrl} ${res.statusCode} - ${duration}ms`);
});
next();
}
}
// 在模块中应用中间件
@Module({})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('*');
}
}
2. 守卫 (Guards)
// auth/guards/jwt-auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class JwtAuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return this.validateRequest(request);
}
private validateRequest(request: any): boolean {
// JWT 验证逻辑
const token = request.headers.authorization?.replace('Bearer ', '');
if (!token) {
return false;
}
// 实际项目中会使用 JWT 服务验证 token
try {
const payload = this.jwtService.verify(token);
request.user = payload;
return true;
} catch {
return false;
}
}
}
// 在控制器中使用
@Controller('users')
@UseGuards(JwtAuthGuard)
export class UsersController {
// ...
}
3. 拦截器 (Interceptors)
// common/interceptors/transform.interceptor.ts
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface Response<T> {
data: T;
timestamp: string;
path: string;
}
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
const request = context.switchToHttp().getRequest();
return next.handle().pipe(
map(data => ({
data,
timestamp: new Date().toISOString(),
path: request.url,
statusCode: context.switchToHttp().getResponse().statusCode,
})),
);
}
}
4. 管道 (Pipes)
// common/pipes/validation.pipe.ts
import {
PipeTransform,
Injectable,
ArgumentMetadata,
BadRequestException
} from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToInstance } 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 = plainToInstance(metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
const messages = errors.map(
error => `${Object.values(error.constraints).join(', ')}`
);
throw new BadRequestException(messages);
}
return value;
}
private toValidate(metatype: Function): boolean {
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}
}
5. 异常过滤器 (Exception Filters)
// common/filters/http-exception.filter.ts
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException
} from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: exception.message,
});
}
}
// 全局应用过滤器
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
配置管理与环境变量
1. 配置模块
// config/config.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import databaseConfig from './database.config';
import appConfig from './app.config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [databaseConfig, appConfig],
envFilePath: `.env.${process.env.NODE_ENV || 'development'}`,
}),
],
})
export class CustomConfigModule {}
// config/database.config.ts
export default () => ({
database: {
host: process.env.DATABASE_HOST || 'localhost',
port: parseInt(process.env.DATABASE_PORT, 10) || 3306,
username: process.env.DATABASE_USERNAME || 'root',
password: process.env.DATABASE_PASSWORD || 'password',
name: process.env.DATABASE_NAME || 'nest_db',
},
});
// config/app.config.ts
export default () => ({
app: {
port: parseInt(process.env.PORT, 10) || 3000,
environment: process.env.NODE_ENV || 'development',
jwtSecret: process.env.JWT_SECRET || 'secret',
},
});
2. 使用配置
// database/database.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
type: 'mysql',
host: configService.get('database.host'),
port: configService.get('database.port'),
username: configService.get('database.username'),
password: configService.get('database.password'),
database: configService.get('database.name'),
entities: [__dirname + '/../**/*.entity{.ts,.js}'],
synchronize: configService.get('app.environment') === 'development',
}),
inject: [ConfigService],
}),
],
})
export class DatabaseModule {}
认证与授权
1. JWT 认证
// auth/auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtStrategy } from './strategies/jwt.strategy';
import { UsersModule } from '../users/users.module';
@Module({
imports: [
UsersModule,
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
secret: configService.get('app.jwtSecret'),
signOptions: { expiresIn: '1d' },
}),
inject: [ConfigService],
}),
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}
// auth/auth.service.ts
import { Injectable, UnauthorizedException } 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(email: string, password: string): Promise<any> {
const user = await this.usersService.findByEmail(email);
if (user && await bcrypt.compare(password, user.password)) {
const { password, ...result } = user;
return result;
}
return null;
}
async login(user: any) {
const payload = { email: user.email, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
user: {
id: user.id,
email: user.email,
username: user.username,
},
};
}
async register(createUserDto: any) {
const hashedPassword = await bcrypt.hash(createUserDto.password, 12);
const user = await this.usersService.create({
...createUserDto,
password: hashedPassword,
});
return this.login(user);
}
}
2. JWT 策略
// auth/strategies/jwt.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: configService.get('app.jwtSecret'),
});
}
async validate(payload: any) {
return {
userId: payload.sub,
email: payload.email
};
}
}
实战项目:博客系统
项目结构
blog-api/
├── src/
│ ├── auth/
│ │ ├── auth.module.ts
│ │ ├── auth.service.ts
│ │ ├── auth.controller.ts
│ │ └── strategies/
│ ├── users/
│ │ ├── users.module.ts
│ │ ├── users.service.ts
│ │ ├── users.controller.ts
│ │ ├── user.entity.ts
│ │ └── dto/
│ ├── posts/
│ │ ├── posts.module.ts
│ │ ├── posts.service.ts
│ │ ├── posts.controller.ts
│ │ ├── post.entity.ts
│ │ └── dto/
│ ├── categories/
│ │ ├── categories.module.ts
│ │ ├── categories.service.ts
│ │ ├── categories.controller.ts
│ │ └── category.entity.ts
│ ├── common/
│ │ ├── filters/
│ │ ├── interceptors/
│ │ ├── pipes/
│ │ └── guards/
│ ├── config/
│ │ ├── config.module.ts
│ │ └── *.config.ts
│ ├── database/
│ │ └── database.module.ts
│ ├── app.module.ts
│ └── main.ts
├── test/
├── .env
├── package.json
└── nest-cli.json
博客实体关系
// posts/post.entity.ts
import {
Entity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
ManyToOne,
ManyToMany,
JoinTable
} from 'typeorm';
import { User } from '../users/user.entity';
import { Category } from '../categories/category.entity';
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column('text')
content: string;
@Column({ default: '' })
excerpt: string;
@Column({ default: 'draft' })
status: string; // draft, published, archived
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
@ManyToOne(() => User, user => user.posts)
author: User;
@ManyToMany(() => Category, category => category.posts)
@JoinTable()
categories: Category[];
}
// categories/category.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToMany } from 'typeorm';
import { Post } from '../posts/post.entity';
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
name: string;
@Column()
description: string;
@ManyToMany(() => Post, post => post.categories)
posts: Post[];
}
博客服务
// posts/posts.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, FindManyOptions } from 'typeorm';
import { Post } from './post.entity';
import { CreatePostDto } from './dto/create-post.dto';
import { UpdatePostDto } from './dto/update-post.dto';
@Injectable()
export class PostsService {
constructor(
@InjectRepository(Post)
private postsRepository: Repository<Post>,
) {}
async findAll(options?: FindManyOptions<Post>): Promise<Post[]> {
return this.postsRepository.find({
...options,
relations: ['author', 'categories'],
});
}
async findOne(id: number): Promise<Post> {
const post = await this.postsRepository.findOne({
where: { id },
relations: ['author', 'categories'],
});
if (!post) {
throw new NotFoundException(`Post with ID ${id} not found`);
}
return post;
}
async create(createPostDto: CreatePostDto, authorId: number): Promise<Post> {
const post = this.postsRepository.create({
...createPostDto,
author: { id: authorId },
});
return this.postsRepository.save(post);
}
async update(id: number, updatePostDto: UpdatePostDto): Promise<Post> {
const post = await this.findOne(id);
Object.assign(post, updatePostDto);
return this.postsRepository.save(post);
}
async remove(id: number): Promise<void> {
const result = await this.postsRepository.delete(id);
if (result.affected === 0) {
throw new NotFoundException(`Post with ID ${id} not found`);
}
}
async findByCategory(categoryId: number): Promise<Post[]> {
return this.postsRepository
.createQueryBuilder('post')
.innerJoin('post.categories', 'category')
.where('category.id = :categoryId', { categoryId })
.leftJoinAndSelect('post.author', 'author')
.leftJoinAndSelect('post.categories', 'categories')
.getMany();
}
}
主应用文件
// main.ts
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { AppModule } from './app.module';
import { TransformInterceptor } from './common/interceptors/transform.interceptor';
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const configService = app.get(ConfigService);
// 全局前缀
app.setGlobalPrefix('api');
// 全局管道
app.useGlobalPipes(new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}));
// 全局拦截器
app.useGlobalInterceptors(new TransformInterceptor());
// 全局过滤器
app.useGlobalFilters(new HttpExceptionFilter());
// 启用 CORS
app.enableCors();
const port = configService.get<number>('app.port') || 3000;
await app.listen(port);
console.log(`Application is running on: http://localhost:${port}`);
}
bootstrap();
测试
1. 单元测试
// users/users.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UsersService } from './users.service';
import { User } from './user.entity';
import { CreateUserDto } from './dto/create-user.dto';
describe('UsersService', () => {
let service: UsersService;
let repository: Repository<User>;
const mockUserRepository = {
create: jest.fn(),
save: jest.fn(),
find: jest.fn(),
findOne: jest.fn(),
update: jest.fn(),
delete: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UsersService,
{
provide: getRepositoryToken(User),
useValue: mockUserRepository,
},
],
}).compile();
service = module.get<UsersService>(UsersService);
repository = module.get<Repository<User>>(getRepositoryToken(User));
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('create', () => {
it('should create a new user', async () => {
const createUserDto: CreateUserDto = {
username: 'testuser',
email: 'test@example.com',
password: 'password123',
};
const savedUser = {
id: 1,
...createUserDto,
createdAt: new Date(),
updatedAt: new Date(),
};
mockUserRepository.create.mockReturnValue(createUserDto);
mockUserRepository.save.mockResolvedValue(savedUser);
const result = await service.create(createUserDto);
expect(result).toEqual(savedUser);
expect(mockUserRepository.create).toHaveBeenCalledWith(createUserDto);
expect(mockUserRepository.save).toHaveBeenCalledWith(createUserDto);
});
});
});
2. E2E 测试
// test/app.e2e-spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
afterAll(async () => {
await app.close();
});
});
部署与生产环境配置
1. 环境配置
# .env.production
NODE_ENV=production
PORT=3000
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USERNAME=your_username
DATABASE_PASSWORD=your_password
DATABASE_NAME=blog_db
JWT_SECRET=your-super-secret-jwt-key
2. Docker 配置
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/main"]
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
depends_on:
- db
restart: unless-stopped
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: blog_db
MYSQL_USER: app_user
MYSQL_PASSWORD: app_password
volumes:
- db_data:/var/lib/mysql
restart: unless-stopped
volumes:
db_data:
3. PM2 配置
// ecosystem.config.js
module.exports = {
apps: [{
name: 'blog-api',
script: 'dist/main.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'development',
},
env_production: {
NODE_ENV: 'production',
},
}],
};
最佳实践
1. 项目结构组织
- 按功能模块组织代码
- 使用清晰的命名约定
- 分离业务逻辑和数据访问层
- 使用 DTO 进行数据验证和传输
2. 性能优化
- 使用数据库索引
- 实现缓存策略
- 优化数据库查询
- 使用压缩和 CDN
3. 安全实践
- 验证所有输入数据
- 使用 Helmet 安全头
- 实现速率限制
- 使用环境变量存储敏感信息
4. 错误处理
- 实现全局异常过滤器
- 使用适当的 HTTP 状态码
- 记录错误日志
- 提供有意义的错误消息
总结
NestJS 是一个功能强大、结构清晰的 Node.js 框架,特别适合构建大型、复杂的服务器端应用程序。通过其模块化架构、依赖注入系统和丰富的生态系统,开发者可以构建出可维护、可测试且高效的应用。
本文涵盖了 NestJS 的核心概念、数据库集成、认证授权、中间件组件以及实际项目开发的全过程。掌握这些知识后,你将能够使用 NestJS 构建出符合企业级标准的应用程序。
随着对框架的深入理解,你可以进一步探索 NestJS 的微服务、GraphQL、WebSocket 等高级特性,不断提升后端开发技能。NestJS 的强类型支持和完整的工具链将为你的开发工作提供强有力的支持。
更多推荐
所有评论(0)