上一篇《构建一个 NestJS 应用程序需要具备哪些基础元素?》里,我们把 Module / Controller / Provider / Guard / Pipe… 这些“组件角色”捋了一遍。
但很多人(包括我一开始)真正卡住的,其实是另一层:这些角色里面提到的名词到底表示啥?
这篇就专门把 NestJS 里常见的英文缩写/名词一次性讲清楚
先给一张速查表(用来对号入座)
| 缩写/名词 | 中文名 | 一句话记住 | 在 NestJS 里常落到哪 |
|---|---|---|---|
| IoC (Inversion of Control) | 控制反转 | “对象怎么创建/怎么组合”交给框架 | @nestjs/core 的容器/加载流程 |
| DI (Dependency Injection) | 依赖注入 | “依赖不要自己 new,框架帮你注入” | Provider 构造函数注入、@Inject() |
| AOP (Aspect-Oriented Programming) | 面向切面编程 | 把日志/鉴权/校验这类横切逻辑“抽出去” | Middleware / Guard / Pipe / Interceptor / Filter |
| DTO (Data Transfer Object) | 数据传输对象 | 传输用的数据结构(尤其是入参) | @Body() + DTO class + ValidationPipe |
| Entity | 实体(实体类) | 持久化模型(数据库表/集合的映射) | TypeORM/Prisma/Mongoose 各自的实体/模型 |
| ORM | 对象关系映射 | “对象 ↔ 表”映射(更广义:把数据库访问抽象成模型/接口) | TypeORM / Prisma(Prisma 更像 ORM-like 的类型安全 DB Client) |
| ODM | 对象文档映射 | “对象 ↔ 文档”映射(Mongo) | Mongoose |
| CRUD | 增删改查 | 最常见的接口形态(创建/查询/更新/删除) | Controller + Service + Repository |
| REST | 表述性状态转移(常说 RESTful) | 用“资源 + HTTP 方法”来组织 API | Controller 的路由设计 |
| HTTP | 超文本传输协议 | Web 接口最常见的传输方式 | @Controller() / @Get() / @Post() |
| RPC | 远程过程调用 | 更像“调用方法”,常见于服务间通信 | @nestjs/microservices |
| RxJS | 响应式扩展 | 响应式编程库(NestJS 部分链路会用到) | Interceptor 里的 pipe(map(...)) |
| Observable | 可观察对象/流 | RxJS 的核心类型(可订阅的异步流) | CallHandler.handle() 返回值 |
| JWT | JSON Web Token(JSON Web 令牌) | 常见的无状态 token 方案 | Guard + Passport Strategy |
| RBAC | 基于角色的访问控制 | “角色 → 权限”的经典权限模型 | Guard(配合装饰器/元数据) |
| CQRS | 命令查询职责分离 | 复杂业务下的读写分离组织方式 | @nestjs/cqrs(可选,不是必需品) |
| CLI | 命令行接口(脚手架) | 生成模板代码、统一项目结构 | @nestjs/cli |
| CORS | 跨域资源共享 | 浏览器跨域访问控制 | app.enableCors() |
| CSRF | 跨站请求伪造 | 利用 Cookie 自动携带发起伪造请求 | 结合鉴权方式 + 中间件/策略设计 |
IoC:Inversion of Control(控制反转)
是什么
在软件工程里,IoC 指把“控制权”(对象创建、生命周期管理、依赖装配、调用时机等)从业务代码转移给框架/容器来统一管理。
它被提出的目的,是降低耦合、集中扩展点、让项目变大后依赖关系仍然可控(而不是到处 new、到处传参)。
一句话:对象的创建与组装不由你手写流程控制,而由框架在启动时统一完成。
适合干啥
- 让项目变大后还能“有秩序地装配依赖”(否则满世界 import/new/传参)
- 让模块边界更清晰:你用“声明”代替“到处调用”
何时使用
在 NestJS 里你基本“自动就在用 IoC”了,因为 NestJS 本身就是基于容器的框架。你需要做的更多是:别破坏它(比如绕过容器到处 new)。
原理(抓重点就行)
- 启动时,NestJS 会扫描模块(Module)、收集 Provider、构建依赖图(谁依赖谁)
- 需要实例化某个类时,由容器按依赖图“自底向上”创建并缓存(默认单例)
伪代码示例
// 你只声明“我需要什么”
@Injectable()
class UserService {
constructor(private repo: UserRepo) {}
}
// 你只声明“这个模块包含哪些”
@Module({ providers: [UserRepo, UserService] })
class UserModule {}
// 剩下的“怎么创建 repo 再创建 service”,框架搞定(IoC)
DI:Dependency Injection(依赖注入)
是什么
DI 是 IoC 的一种常见实现方式,它强调“依赖从外部注入”,而不是在类内部主动创建依赖。
目的很直接:让代码更解耦、依赖更可替换(方便测试/替身实现)、也更容易在大型项目里统一管理。
一句话:把“依赖关系”从代码里的 new,变成声明式注入。
适合干啥
- 让 service 更容易测试(可以替换依赖、注入 mock)
- 避免强耦合(service 不需要知道 repo 如何创建)
何时使用
你写 NestJS 业务时,绝大多数依赖都推荐走 DI:
- service 依赖 repo / http client / config / logger
- guard 依赖 auth service
- interceptor 依赖 cache service
原理
DI 的核心是“token → provider”的映射:
- 最常见 token 就是 class 本身(
UserService) - 容器根据构造函数参数的类型/注入 token,找到对应 provider,注入实例
伪代码示例
@Injectable()
class UserService {
constructor(private readonly repo: UserRepo) {}
}
// 自定义 token(比如注入第三方库实例)
const REDIS = Symbol('REDIS');
@Module({
providers: [
{ provide: REDIS, useValue: /* redisClient */ {} },
{ provide: UserRepo, useClass: UserRepo },
],
})
class InfraModule {}
@Injectable()
class CacheService {
constructor(@Inject(REDIS) private redis: any) {}
}
AOP:Aspect-Oriented Programming(面向切面)
是什么
AOP 是一种把“横切关注点”(logging、auth、validation、metrics 等)从核心业务逻辑中分离出来的思想,常见手段是拦截/代理,在函数执行前后“织入”通用逻辑。
提出它的目的,是减少重复代码、统一策略,让业务代码更专注在“做业务”。
一句话:把“到处都要做”的横切逻辑(日志/鉴权/校验/统一返回)从业务代码里抽出来。
适合干啥
- 统一做日志、耗时统计、异常格式、权限校验
- 让 controller/service 更“干净”,专注业务
何时使用(别上来就 AOP 过度)
当你发现同一段逻辑在 N 个接口里重复出现时,AOP 才开始值钱。
如果只是一个接口的特殊处理,直接写在 handler 里往往更直观。
原理(用 NestJS 的话来讲)
NestJS 不是“只有一种 AOP”,它把 AOP 拆成几类工具,各司其职:
- Middleware:请求刚进门(路由前)
- Guard:能不能进(鉴权/权限)
- Pipe:入参校验/转换
- Interceptor:前后包一层(统一返回/缓存/耗时)
- Exception Filter:统一错误输出
它们共同点是:不改业务函数签名,也能在请求链路上插入逻辑。
伪代码示例
// 统一返回结构:{ code, data, traceId }
@Injectable()
class WrapResponseInterceptor implements NestInterceptor {
intercept(ctx, next) {
const traceId = /* get from request */ 'xxx';
return next.handle().pipe(map(data => ({ code: 0, data, traceId })));
}
}
// 鉴权:没 token 就不让进
@Injectable()
class AuthGuard implements CanActivate {
canActivate(ctx) {
const req = ctx.switchToHttp().getRequest();
return Boolean(req.headers.authorization);
}
}
DTO:Data Transfer Object(数据传输对象)
是什么
DTO 常用于分层架构/分布式系统的“边界处”,用来描述数据在层与层(或服务与服务)之间传输的结构。它强调“只承载数据”,不强调业务行为。
提出它的目的,是把外部输入/输出与内部模型隔离开:API 契约清晰、不把内部 Entity/领域对象直接暴露出去。
一句话:专门用来“接收/传输”的数据结构(尤其是“请求入参”)。
适合干啥
- 把“接口入参长啥样”讲清楚(更容易维护)
- 配合校验:让脏数据在进业务之前就被拦住
何时使用
只要是对外接口(HTTP/RPC/GraphQL)基本都建议用 DTO:
CreateUserDto、UpdateUserDto、QueryUserDto
原理(别纠结细节,抓住链路)
典型链路是:@Body() 拿到原始对象 → ValidationPipe 校验/转换 → 传入 controller 方法参数。
常见组合是 class-validator + class-transformer(你会在项目里看到它们)。
伪代码示例
class CreateUserDto {
// @IsEmail()
email: string;
// @MinLength(8)
password: string;
}
@Controller('users')
class UserController {
@Post()
create(@Body() dto: CreateUserDto) {
return this.userService.create(dto);
}
}
// main.ts 里常见开启方式
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
Entity:实体(通常指持久化实体)
是什么
在不同语境里 Entity 有两层含义:
- 在 DDD 里,Entity 是“有持续身份(identity)的对象”,即便属性变了,它仍然是同一个实体
- 在 ORM 里,Entity 往往指“表/集合的映射模型”(实体类/模型定义)
提出它的目的,是用更结构化的方式表达持久化数据(字段/关系/约束),让数据访问与演进更可维护。
一句话:“数据库里的数据结构”在代码里的对应物。
注意:Entity 本身不是 NestJS 独有,它更多来自你选的 ORM/ODM。
适合干啥
- 在数据库层做结构化建模(表字段、索引、关系)
- 让查询/写入更可维护(至少比字符串拼 SQL 好维护一些)
何时使用
取决于你选型:
- 用 TypeORM:你会写
@Entity()这类实体类 - 用 Prisma:你写的是
schema.prisma的模型(更像“模型定义 + 类型安全访问层”,不一定叫 Entity,但在项目里的角色类似) - 用 Mongoose:你会写 Schema/Model(更偏 ODM)
原理(大方向)
Entity/Model 提供“结构 + 映射 + 生命周期(可选)”,最终目标是:把 DB 操作封装成更可控的接口。
伪代码示例(以 TypeORM 思路举例)
@Entity('users')
class UserEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
email: string;
}
@Injectable()
class UserRepo {
constructor(/* inject repository */) {}
}
ORM(对象关系映射)/ ODM(对象文档映射):到底差在哪?
ORM(Object-Relational Mapping,对象关系映射)
ORM 通过把“关系型数据库的表/行/关系”映射为代码里的对象/模型,让你用更高层的接口去读写数据。
提出它的目的,是减少手写 SQL 的重复劳动,让数据访问更结构化、更易演进(当然也会带来一定抽象成本)。
- 面向关系型数据库(MySQL/PostgreSQL…)
- 关键词:表、行、关联、事务
ODM(Object-Document Mapping,对象文档映射)
ODM 把“文档数据库的文档/集合/嵌套结构”映射为代码里的对象/模型,帮助你更一致地读写 MongoDB 等文档型存储。
目的类似 ORM:让数据结构和访问方式更可控,只是底层存储模型不同。
- 面向文档型数据库(MongoDB…)
- 关键词:文档、集合、嵌套结构
何时用哪一个
别纠结名词,按存储选型走:你用 MySQL/PG 就看 ORM,你用 Mongo 就看 ODM。
CRUD(增删改查)/ REST(表述性状态转移):为什么老看到它俩一起出现?
CRUD 是什么
CRUD 是持久化系统里最常见的一组基础操作集合(Create / Read / Update / Delete)。
提出它的目的,是用一套通用词汇把“增删改查”这类需求说清楚,方便分工、设计接口与评审。
一句话:Create / Read / Update / Delete,增删改查。
REST 是什么
REST(Representational State Transfer)最初是 Roy Fielding 在博士论文里提出的一种架构风格,用“资源 + 表述 + 约束”来组织系统交互。
它的目的,是让 API 的语义更统一、可被 HTTP 的缓存/状态码等机制更好地利用。
一句话:用“资源 + HTTP 方法”来组织 API(比如 /users/:id + GET/POST/PUT/DELETE)。
在 NestJS 里怎么体现
CRUD 更像“你要做的事”,REST 更像“你怎么设计接口”。
NestJS 的 Controller 很适合写 REST 风格的 CRUD(但你也可以写 RPC 风格,不强制)。
@Controller('users')
class UserController {
@Post() create(@Body() dto) {}
@Get(':id') findOne(@Param('id') id) {}
@Put(':id') update(@Param('id') id, @Body() dto) {}
@Delete(':id') remove(@Param('id') id) {}
}
JWT(JSON Web Token)/ RBAC(基于角色的访问控制):鉴权和权限常见两套缩写
JWT(JSON Web Token,JSON Web 令牌)
JWT 是一种开放标准(RFC 7519)定义的令牌格式,用 JSON 结构承载声明(claims),常见形式是 header.payload.signature。
它被广泛使用的目的,是让身份信息在服务之间可传递、可验证(尤其适合前后端分离/多服务场景)。
一句话:一种无状态 token(常用来承载登录态/身份声明)。
什么时候用:前后端分离、移动端、需要跨服务验证身份时很常见。
什么时候别硬上:你如果只是内部小系统、单体应用,Session 也完全可以。
伪代码(NestJS 常见落点):
// 认证:验证 token 的逻辑一般放 Guard / Strategy
@UseGuards(JwtAuthGuard)
@Get('me')
me(@Req() req) {
return req.user;
}
RBAC(Role-Based Access Control,基于角色的访问控制)
RBAC 是经典的访问控制模型,用“角色(Role)”作为权限分配与授权的中间层(用户 ↔ 角色 ↔ 权限)。
目的,是让权限管理更可维护:你通常给用户分配角色,而不是给每个用户逐条配置权限。
一句话:按角色控制权限(admin/user…)。
落点通常是 Guard + 装饰器元数据:
@SetMetadata('roles', ['admin'])
@UseGuards(AuthGuard, RolesGuard)
@Delete(':id')
remove() {}
CQRS:Command Query Responsibility Segregation(命令查询职责分离,常被简化成“读写分离”)
是什么
CQRS 提倡把“改变系统状态的命令(Command)”与“读取系统状态的查询(Query)”分离成不同的模型/路径。
提出它的目的,是在复杂业务里隔离读写关注点:写侧强调规则与一致性,读侧强调展示与查询效率(但也会增加架构复杂度)。
一句话:把“写操作(Command)”和“读操作(Query)”拆成不同模型/流程。
适合干啥
- 复杂业务、读写模型差异巨大时,能让代码更清晰
- 事件驱动/审计需求强时更常见
何时使用
别上来就 CQRS。通常是项目复杂到:
- service 越写越像一坨“超级函数”
- 写操作需要强规则/强审计
- 读操作需要高度定制的视图模型
再考虑引入(NestJS 有
@nestjs/cqrs可选)。
HTTP(超文本传输协议)/ RPC(远程过程调用):请求进来到底是哪条路?
是什么
HTTP 是应用层网络协议,用于客户端与服务端之间的请求/响应通信;RPC 是一种远程调用模型,强调“像调用本地函数一样调用远端能力”。
把它们放在一起讲的目的,是让你明确 NestJS 不只做 HTTP:它既能做 Web API,也能做微服务通信。
- HTTP:最常见的 Web 接口方式(REST/JSON 都算在这里面)
- RPC:更像“调用一个方法”,不强制资源风格(常见于微服务通信)
适合干啥
- 你做 BFF / Web API:基本就是 HTTP
- 你做服务拆分、服务间通信:RPC(或者消息队列)会更常见
何时使用(说人话)
小中型系统,先把 HTTP 写顺就行;
只有当你确实遇到“服务间调用多、边界清晰、协议要统一”时,再去考虑 RPC/Microservices 那套。
原理(在 NestJS 里怎么体现)
- HTTP:
@Controller()+@Get()/@Post()这一套装饰器,走的是平台适配层(Express/Fastify) - RPC:通常用
@nestjs/microservices,通过 transport(TCP/Redis/NATS/Kafka…)收发消息
伪代码示例
// HTTP(你已经很熟了)
@Controller('users')
class UserController {
@Get(':id')
findOne(@Param('id') id: string) {}
}
// RPC(示意):message pattern 触发一个 handler
// @MessagePattern({ cmd: 'user.findOne' })
// findOne(payload) {}
RxJS(响应式扩展)/ Observable(可观察对象):为啥 NestJS 老出现 rxjs?
是什么
RxJS 是基于 Observable 的响应式编程库,用来表达“时间维度上的数据流”;Observable 则是可订阅的异步流抽象。
它们出现的目的,是更自然地处理“流式、可组合、可取消/重试”的异步场景(NestJS 的部分执行链路也选择了它作为抽象)。
- RxJS:响应式编程库
- Observable:RxJS 的核心数据类型(可以理解成“可订阅的异步流”)
适合干啥
你不用为了 NestJS 去“强行学响应式编程”。它主要在两块很常见:
- 拦截器/管道链路:
next.handle().pipe(map(...))这种写法(你在 Interceptor 里已经见过) - 流式/事件式场景:SSE、WebSocket、某些需要持续推送的接口
何时使用
- 你只写普通 CRUD:Promise/async-await 够用,别硬上 Observable
- 你需要“组合多个异步来源”“做流式处理/取消/重试”:Observable 就很香
原理(够用版)
NestJS 允许 controller 返回:
- primitive / object(直接返回)
Promise<T>(等 promise resolve)Observable<T>(内部订阅,取最终值/流)
Interceptor 里之所以经常用 RxJS,是因为 CallHandler.handle() 返回的就是 Observable。
伪代码示例
// 典型 interceptor:对返回值做 map
intercept(ctx, next) {
return next.handle().pipe(
map(data => ({ code: 0, data })),
);
}
// controller 返回 Observable(示意)
@Get()
list() {
// return from([1, 2, 3]);
}
CLI:Command Line Interface(命令行接口/脚手架)
是什么
CLI(Command Line Interface)是通过命令行与工具交互的一种方式;在 NestJS 语境下通常特指官方脚手架,用于生成模板代码与维护约定结构。
它的目的,是减少重复劳动、统一项目结构,让团队协作更顺。
一句话:帮你生成模板代码、少手敲一些重复文件。
适合干啥
- 新建 module/controller/service
- 一键生成资源(CRUD 模板),并把文件结构按约定摆好
何时使用
你如果是团队项目,建议统一用 CLI 生成骨架,代码风格更一致;
个人练手也可以不用,但熟悉一下常用命令挺省事。
原理(不深究)
CLI 本质是代码生成器(schematics),按模板生成文件 + 更新模块引用。
伪代码示例(命令示意)
nest g module user
nest g controller user
nest g service user
# nest g resource user # 想要 CRUD 模板时再用
CORS(跨域资源共享)/ CSRF(跨站请求伪造):Web 安全里最常被混淆的两个缩写
它们是什么
CORS 是浏览器同源策略下的一套“跨域放行机制”(通过响应头协商);CSRF 是一种利用浏览器自动携带 Cookie 的攻击方式。
把它俩放一起的目的,是提醒你:跨域(CORS)和伪造请求(CSRF)是两件事,不要混着处理。
- CORS(Cross-Origin Resource Sharing):浏览器的跨域访问控制(“能不能从别的域来调用我”)
- CSRF(Cross-Site Request Forgery):利用浏览器自动带 Cookie 的特性发起伪造请求(“我是不是被借刀了”)
适合干啥
- CORS:前后端分离、不同域名端口开发时必须处理
- CSRF:当你用 Cookie 维持登录态 且接口会产生副作用(转账/下单/改资料)时,需要重点考虑
何时使用(简化判断)
- 你用
Authorization: Bearer <token>(JWT)这类 header 携带 token:CSRF 风险通常更低(但不代表“完全不用管安全”) - 你用 Cookie + Session:CSRF 基本要纳入设计(同站策略、token、双重提交等)
原理(够用版)
- CORS:是浏览器限制,你服务器得返回合适的响应头
- CSRF:是攻击方式,你得让“跨站伪造请求”失效
伪代码示例
// CORS:NestJS 常见开启方式(示意)
app.enableCors({
origin: ['https://your-frontend.example'],
credentials: true,
});
最后
如果你现在只想快速上手 NestJS,我建议按这个顺序消化:
- 先把 IoC / DI 吃透(这决定你后面写代码是不是舒服)
- 再把 AOP 在 NestJS 的落点(Guard/Pipe/Interceptor/Filter)对上请求链路
- 然后用 DTO + ValidationPipe 把“入参质量”先稳住
- 至于 Entity/ORM/ODM、JWT/RBAC、CQRS,就按项目需要逐步加,不用一次性把工具箱搬回家
以上是我学习 NestJS 过程中的一些整理与理解,欢迎在评论区补充/讨论;如果哪里有偏差,也欢迎直接指出,我会及时修正。