NestJS Node 企业级框架
bash npm install -g @nestjs/cli
安装完成后,使用 `nest` 命令验证:
```bash
nest --version
创建你的第一个项目
使用 CLI 创建新项目:
nest new project-name
CLI 会询问选择包管理器(npm 或 yarn),随后自动安装依赖。进入项目目录并启动开发服务器:
cd project-name
npm run start:dev
打开浏览器访问 http://localhost:3000,你会看到 Hello World! 的欢迎消息。
理解核心概念
NestJS 应用程序的基石是三个核心概念:模块、控制器和提供者,它们共同构建出整洁的分层架构。
模块 (Modules)
模块是用 @Module() 装饰器注解的类,用于组织相同功能域的一组组件。每个 NestJS 应用至少有一个根模块 AppModule。
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [], // 导入其他模块
controllers: [AppController], // 该模块拥有的控制器
providers: [AppService], // 该模块拥有的提供者
})
export class AppModule {}
控制器 (Controllers)
控制器负责处理传入的 HTTP 请求并返回响应。通过 @Controller() 装饰器和 HTTP 方法装饰器(@Get、@Post 等)定义路由。
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller('cats') // 路由前缀 /cats
export class CatsController {
constructor(private readonly appService: AppService) {}
@Get()
findAll(): string {
return this.appService.getHello();
}
}
提供者 (Providers) 与依赖注入
提供者是可以被注入的对象,通常是服务类。它们用 @Injectable() 装饰器标记,并通过构造器注入到控制器或其他提供者中。
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
NestJS 的 IoC 容器会自动管理这些实例的创建与生命周期,确保单一实例(默认)在多处共享。
构建 RESTful API
路由与请求处理
常用的装饰器包括:
| 装饰器 | 说明 |
|---|---|
@Get() |
处理 GET 请求 |
@Post() |
处理 POST 请求 |
@Put() |
处理 PUT 请求 |
@Delete() |
处理 DELETE 请求 |
@Param() |
获取路径参数,例如 /cats/:id |
@Query() |
获取查询字符串参数 |
@Body() |
获取请求体(POST/PUT) |
创建 CRUD 示例
我们以“猫咪”资源为例,创建一个完整的增删改查。
1. 生成模块、控制器和服务
nest generate module cats
nest generate controller cats
nest generate service cats
2. 定义数据传输对象 (DTO)
创建 create-cat.dto.ts:
export class CreateCatDto {
name: string;
age: number;
breed: string;
}
3. 实现服务 (cats.service.ts)
import { Injectable } from '@nestjs/common';
import { CreateCatDto } from './create-cat.dto';
@Injectable()
export class CatsService {
private readonly cats: CreateCatDto[] = [];
create(cat: CreateCatDto) {
this.cats.push(cat);
}
findAll(): CreateCatDto[] {
return this.cats;
}
findOne(id: number): CreateCatDto {
return this.cats[id];
}
}
4. 实现控制器 (cats.controller.ts)
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { CatsService } from './cats.service';
import { CreateCatDto } from './create-cat.dto';
@Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}
@Post()
create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
return '猫咪已添加';
}
@Get()
findAll() {
return this.catsService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.catsService.findOne(+id);
}
}
现在你可以通过 POST /cats 添加猫咪,GET /cats 获取列表,GET /cats/1 获取单个猫咪。
进阶特性
中间件
中间件是在请求进入路由处理程序之前执行的函数,可用于日志记录、CORS 设置等。实现 NestMiddleware 接口。
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log(`Request... ${req.method} ${req.url}`);
next();
}
}
在模块中应用:
export class CatsModule implements NestMiddleware {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('cats');
}
}
异常过滤器
内置的异常层负责处理应用中抛出的所有异常。你也可以创建自定义异常过滤器,用于改写错误响应格式。
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = exception.getStatus();
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
message: exception.message,
});
}
}
在控制器或全局应用:
@UseFilters(new HttpExceptionFilter())
@Controller('cats')
export class CatsController { ... }
管道 (Pipes)
管道用于转换或验证输入数据。例如,内置的 ValidationPipe 配合 class-validator 可以自动校验请求体。
安装依赖:
npm install class-validator class-transformer
在 main.ts 中全局启用:
app.useGlobalPipes(new ValidationPipe());
DTO 中添加验证规则:
import { IsString, IsInt } from 'class-validator';
export class CreateCatDto {
@IsString()
name: string;
@IsInt()
age: number;
@IsString()
breed: string;
}
守卫 (Guards)
守卫用于决定请求是否由路由处理程序处理,常用于权限认证。实现 CanActivate 接口。
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
return request.headers.authorization === 'secret';
}
}
在控制器或路由上使用 @UseGuards(AuthGuard)。
拦截器 (Interceptors)
拦截器可以在方法执行前后添加额外逻辑,例如统一响应格式、日志记录、缓存等。
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(map(data => ({ code: 0, data })));
}
}
全局应用:app.useGlobalInterceptors(new TransformInterceptor())
数据库集成
通常企业级应用需要持久化存储。NestJS 官方推荐使用 TypeORM 或 Prisma。以 TypeORM 为例:
安装依赖:
npm install @nestjs/typeorm typeorm mysql2
在根模块中配置连接:
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'test',
entities: [Cat],
synchronize: true, // 生产环境务必关闭
}),
],
})
export class AppModule {}
定义实体并使用 @nestjs/typeorm 的 InjectRepository 装饰器在服务中操作数据库。详细使用可参考 TypeORM 官方文档。
测试
Nest 内置 Jest,可通过 CLI 一键生成测试文件。
npm run test # 单元测试
npm run test:e2e # 端到端测试
示例:测试 CatsController。
describe('CatsController', () => {
let controller: CatsController;
let service: CatsService;
beforeEach(async () => {
const module = await Test.createTestingModule({
controllers: [CatsController],
providers: [CatsService],
}).compile();
controller = module.get(CatsController);
service = module.get(CatsService);
});
it('should return an array of cats', () => {
jest.spyOn(service, 'findAll').mockImplementation(() => [{ name: 'Kitty', age: 2, breed: '未知' }]);
expect(controller.findAll()).toEqual([{ name: 'Kitty', age: 2, breed: '未知' }]);
});
});