跳到主要内容

NestJS 完整教程(新手向)

阅读需 8 分钟

本教程面向零基础或刚接触 NestJS 的开发者,按由浅入深的顺序讲解所有主要知识点。建议按章节顺序阅读。


一、NestJS 简介

什么是 NestJS?

NestJS 是一个基于 Node.js服务端框架,使用 TypeScript 编写,架构风格借鉴了 Angular。特点包括:

  • 分层清晰:控制器、服务、模块等概念明确,适合中大型项目
  • 依赖注入(DI):内置 IoC 容器,便于测试与解耦
  • TypeScript 优先:类型安全、装饰器驱动
  • 可扩展:支持 REST、GraphQL、WebSocket、微服务等
  • 生态丰富:与 TypeORM、Prisma、Passport、class-validator 等无缝集成

适合做什么?

  • RESTful API / BFF(Backend for Frontend)
  • GraphQL 服务
  • 微服务(配合 Kafka、RabbitMQ 等)
  • 实时应用(WebSocket、SSE)
  • 需要严格架构与可维护性的 Node 后端

前置知识

  • JavaScript/TypeScript 基础
  • Node.js 与 npm 基本使用
  • 了解 HTTPREST 概念更佳

二、环境准备与创建项目

环境要求

  • Node.js:18.x 及以上(建议 20 LTS)
  • 包管理器:npm / yarn / pnpm

使用 CLI 创建项目

安装 Nest CLI(可选,也可用 npx 不全局安装):

npm i -g @nestjs/cli
nest new my-app

或直接用 npx:

npx @nestjs/cli new my-app

按提示选择包管理器(npm / yarn / pnpm),完成后进入项目并启动:

cd my-app
npm run start

浏览器访问 http://localhost:3000 应看到 Hello World!

常用脚本

命令说明
npm run start开发模式(默认端口 3000)
npm run start:dev监听文件变化自动重启
npm run start:debug调试模式
npm run build编译为 JavaScript 到 dist/
npm run start:prod以生产模式运行 dist/
开发时建议

日常开发用 npm run start:dev,保存即热重载。


三、项目目录结构

my-app/
├── src/
│ ├── app.module.ts # 根模块
│ ├── app.controller.ts # 根控制器
│ ├── app.service.ts # 根服务
│ └── main.ts # 入口文件,引导 Nest 应用
├── test/ # 测试
├── nest-cli.json # Nest CLI 配置
├── tsconfig.json
└── package.json
  • main.ts:创建 NestFactory.create(AppModule)、监听端口、可配置全局管道/前缀等
  • app.module.ts:根模块,通过 imports 聚合其他模块
  • app.controller.ts / app.service.ts:根控制器与根服务,演示最基础的请求处理与业务逻辑

后续会按功能拆成多个模块(如 usersposts),每个模块可有自己的 *.module.ts*.controller.ts*.service.ts


四、核心概念:模块、控制器、服务

4.1 模块(Module)

模块是组织代码的单元,把控制器、服务、其他模块组织在一起。

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
imports: [], // 其他模块
controllers: [AppController],
providers: [AppService],
exports: [AppService], // 导出后,其他模块可注入 AppService
})
export class AppModule {}
  • imports:引入其他模块(使用其 exports)
  • controllers:该模块下的控制器(处理 HTTP 等)
  • providers:该模块下的可注入服务
  • exports:把 providers 暴露给其他模块

4.2 控制器(Controller)

控制器负责处理入站请求并返回响应,对应路由与 HTTP 方法。

// app.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { AppService } from './app.service';

@Controller() // 路由前缀为空,即根
export class AppController {
constructor(private readonly appService: AppService) {}

@Get()
getHello(): string {
return this.appService.getHello();
}

@Get('info')
getInfo() {
return { name: 'My App', version: '1.0' };
}
}
  • @Controller('prefix'):该控制器下所有路由共用的前缀,如 @Controller('users') 则路由为 /users/...
  • @Get()@Post()@Put()@Patch()@Delete():对应 HTTP 方法
  • constructor 中注入 AppService:由 Nest 的依赖注入提供实例

4.3 服务(Provider / Service)

服务承载业务逻辑,被控制器注入并调用,避免在控制器里写复杂逻辑。

// app.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
  • @Injectable():表示该类可由 Nest 的 DI 容器管理,并注入到其他类中

关系小结:请求 → 控制器(路由)→ 调用服务(业务)→ 返回响应。


五、依赖注入(DI)

Nest 内置 IoC 容器:你只声明「需要什么类」,容器负责创建实例并注入。

@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
// ↑ 私有只读 ↑ 类型声明即可,Nest 自动注入实例
}
  • Provider:在某个模块的 providers 里声明(如 AppService
  • 注入:在控制器或其它服务的 constructor 中写上类型,Nest 会注入该模块或已导入模块的 exports 中的对应实例
  • 作用域:默认单例;可按需使用 Scope.REQUEST 等实现请求级实例

六、路由与请求处理

6.1 路径与参数

@Controller('users')
export class UsersController {
@Get() // GET /users
findAll() { return []; }

@Get(':id') // GET /users/123
findOne(@Param('id') id: string) {
return { id };
}

@Post()
create(@Body() body: CreateUserDto) {
return body;
}

@Put(':id')
update(@Param('id') id: string, @Body() body: UpdateUserDto) {
return { id, ...body };
}

@Delete(':id')
remove(@Param('id') id: string) {
return { deleted: id };
}
}

常用参数装饰器:

装饰器含义
@Param('id')路径参数
@Query('key')查询参数
@Body()请求体(JSON)
@Headers('name')请求头
@Req() / @Res()原生 request / response(慎用,会失去部分 Nest 能力)

6.2 全局路由前缀

main.ts 中可为所有路由加前缀:

async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api'); // 所有路由变为 /api/...
await app.listen(3000);
}

七、DTO 与验证(Validation)

7.1 为什么用 DTO

  • 定义请求体的形状,便于类型提示和文档
  • 配合 class-validator自动校验,非法请求在进入控制器前被拦截

7.2 安装与启用

npm i class-validator class-transformer

main.ts 中启用全局验证管道:

import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({
whitelist: true, // 自动去掉 DTO 中未声明的属性
forbidNonWhitelisted: true, // 若带未声明属性则直接 400
transform: true, // 按类型自动转换(如 string → number)
}));
await app.listen(3000);
}

7.3 定义 DTO

// create-user.dto.ts
import { IsString, IsEmail, MinLength, IsOptional } from 'class-validator';

export class CreateUserDto {
@IsString()
@MinLength(2)
name: string;

@IsEmail()
email: string;

@IsString()
@MinLength(6)
password: string;

@IsOptional()
@IsString()
avatar?: string;
}

控制器中直接使用即可,校验失败时 Nest 自动返回 400 与错误信息:

@Post()
create(@Body() dto: CreateUserDto) {
return this.usersService.create(dto);
}

八、管道(Pipe)

管道用于转换或校验输入数据,在到达控制器方法之前执行。

8.1 内置管道示例

  • ValidationPipe:上面已用,配合 class-validator
  • ParseIntPipe:将参数转为整数,否则 400
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.usersService.findOne(id);
}

8.2 自定义管道

实现 PipeTransform 接口即可:

// parse-bool.pipe.ts
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';

@Injectable()
export class ParseBoolPipe implements PipeTransform<string, boolean> {
transform(value: string): boolean {
if (value === 'true') return true;
if (value === 'false') return false;
throw new BadRequestException('Expected "true" or "false"');
}
}

在参数或控制器上使用:@Query('active', ParseBoolPipe) active: boolean


九、守卫(Guard)

守卫决定是否允许请求继续,常用于权限、角色校验。

9.1 使用方式

  • 控制器级:@UseGuards(RolesGuard)
  • 方法级:同上
  • 全局:app.useGlobalGuards(new RolesGuard())

9.2 简单示例

// roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}

canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler());
if (!requiredRoles) return true;
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((role) => user?.roles?.includes(role));
}
}

通过 Reflector 读取元数据(如自定义装饰器 @Roles('admin')),再根据 user 判断。若返回 false,Nest 默认返回 403。


十、拦截器(Interceptor)

拦截器可在请求前/后统一处理逻辑,如日志、超时、响应格式封装等。

10.1 响应映射示例

// 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;
}

@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
return next.handle().pipe(
map((data) => ({
data,
timestamp: new Date().toISOString(),
})),
);
}
}

main.ts 中全局注册:app.useGlobalInterceptors(new TransformInterceptor());,则所有控制器返回的数据会被包成 { data, timestamp }

10.2 典型用途

  • 统一包装 { code, data, message }
  • 日志、耗时统计
  • 超时控制(timeout RxJS 操作符)

十一、异常与异常过滤器

11.1 内置 HTTP 异常

在服务或控制器中直接抛出,Nest 会转为对应状态码的 JSON:

import { NotFoundException, BadRequestException } from '@nestjs/common';

throw new NotFoundException('用户不存在');
throw new BadRequestException('参数错误');

常用:BadRequestException(400)、UnauthorizedException(401)、ForbiddenException(403)、NotFoundException(404) 等。

11.2 异常过滤器(Exception Filter)

若希望统一格式或对特定异常做特殊处理,可写异常过滤器:

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception.getStatus();
const result = exception.getResponse();
response.status(status).json({
statusCode: status,
message: typeof result === 'object' ? (result as any).message : result,
timestamp: new Date().toISOString(),
});
}
}

使用:@UseFilters(HttpExceptionFilter)app.useGlobalFilters(new HttpExceptionFilter())


十二、中间件(Middleware)

中间件在路由处理器之前执行,可做日志、解析、限流等。

12.1 定义与注册

// 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) {
console.log(`${req.method} ${req.url}`);
next();
}
}

在模块中挂到指定路由:

export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('*');
}
}

12.2 函数式中间件

若无需依赖注入,可写为普通函数,在 consumer.apply(LoggerMiddleware) 处传入即可。


十三、配置(ConfigModule)

13.1 使用 @nestjs/config

npm i @nestjs/config

根模块中引入:

import { ConfigModule } from '@nestjs/config';

@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env.local', '.env'],
}),
],
})
export class AppModule {}

13.2 读取配置

在服务或控制器中注入 ConfigService

constructor(private config: ConfigService) {}

someMethod() {
const port = this.config.get<number>('PORT', 3000);
const dbUrl = this.config.get<string>('DATABASE_URL');
}

推荐配合 .env 文件,不要提交敏感信息。


十四、数据库(TypeORM 简介)

Nest 与 TypeORMPrisma 等都能很好集成,这里以 TypeORM 为例说明「模块 + 实体」的用法。

14.1 安装

npm i @nestjs/typeorm typeorm mysql2

14.2 注册与实体

// app.module.ts
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './users/user.entity';

@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'xxx',
database: 'mydb',
entities: [User],
synchronize: true, // 开发时可 true,生产建议 false + 迁移
}),
TypeOrmModule.forFeature([User]), // 在 UsersModule 中
],
})
export class AppModule {}

14.3 实体与在服务中注入 Repository

// user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;

@Column()
name: string;

@Column({ unique: true })
email: string;
}
// users.service.ts
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private userRepo: Repository<User>,
) {}

async findAll(): Promise<User[]> {
return this.userRepo.find();
}
}

更多(关系、迁移、事务)请查阅 TypeORM 文档


十五、认证(JWT 示例)

认证涉及「登录签发 token」和「请求时校验 token」,常用 Passport + JWT

15.1 安装

npm i @nestjs/passport @nestjs/jwt passport passport-jwt passport-local
npm i -D @types/passport-jwt @types/passport-local

15.2 思路简述

  • 登录:用 LocalStrategy 校验用户名密码,通过后用 JwtService.sign() 签发 token,返回给前端。
  • 受保护路由:用 JwtStrategy 从 Header 的 Bearer token 中解析出用户信息,挂到 request.user;再用 Guard 在需要登录的接口上使用 JwtAuthGuard

15.3 配置与使用

在模块中注册:

JwtModule.register({
secret: process.env.JWT_SECRET || 'your-secret',
signOptions: { expiresIn: '7d' },
}),
PassportModule,

在需要登录的控制器或方法上添加:

@UseGuards(JwtAuthGuard)
@Get('profile')
getProfile(@Request() req) {
return req.user;
}

具体实现可参考官方文档 NestJS 认证


十六、测试

16.1 单元测试(服务)

使用 Jest,对服务类进行隔离测试,依赖用 mock:

describe('AppService', () => {
let service: AppService;

beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [AppService],
}).compile();
service = module.get<AppService>(AppService);
});

it('getHello', () => {
expect(service.getHello()).toBe('Hello World!');
});
});

16.2 E2E 测试

对完整 HTTP 请求做测试:

describe('AppController (e2e)', () => {
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});

运行:npm run test(单元)、npm run test:e2e(E2E)。


十七、构建与部署

17.1 构建

npm run build

产物在 dist/,为编译后的 JavaScript。

17.2 生产运行

npm run start:prod

会执行 node dist/main(或你配置的入口)。生产环境建议用 PM2Docker 或云平台的 Node 运行时,并设置好环境变量(如 NODE_ENV=production、数据库与 JWT 的配置)。

17.3 部署注意点

  • 使用 环境变量 管理配置,不要写死敏感信息
  • 数据库用 迁移 管理表结构,不要依赖 synchronize: true
  • 可配合 Nginx 做反向代理与静态资源
  • 健康检查可暴露一个 /health 接口供负载均衡使用

十八、学习路径小结

  1. 入门:创建项目 → 理解 Module / Controller / Service → 改根路由看效果
  2. 请求:路由、@Param / @Query / @Body、DTO + ValidationPipe
  3. 结构:按功能拆模块(如 UsersModule)、依赖注入与 exports
  4. 增强:管道、守卫、拦截器、异常过滤器、中间件
  5. 数据:ConfigModule、TypeORM/Prisma、实体与 Repository
  6. 安全:JWT 认证、角色守卫
  7. 质量:单元测试、E2E 测试
  8. 上线:build、环境变量、进程管理、健康检查

遇到问题可查阅 NestJS 官方文档

Loading Comments...