NestJS学习
前言
最近花时间学了 NestJS,从一个「听说过但没碰过」的状态,到能搭出一个完整的后端项目。这篇东西算是一个学习记录,把过程中的理解、踩过的坑、对框架的感悟写下来,也算是对自己这段时间的一个交代。
我对 NestJS 的理解
刚接触的时候,最直观的感受是:它和传统的 Node.js 框架不太一样。
以前写 Express ,都是"需要什么自己加",很灵活,但也容易写散。NestJS 给我的感觉是 —— 它把规矩定好了。模块怎么拆、代码放哪里、请求怎么流转,都有一套明确的规范。
刚开始觉得有点束缚,但写多了发现,这种约束其实是一种解放。项目结构不会随着代码量增加而腐烂,回头看自己写的代码,思路是清晰的。
项目结构:模块化是核心
学 NestJS 最先要理解的就是模块化。每个功能都是一个模块,模块里放控制器、服务、数据实体,各司其职:
src/
├── main.ts # 应用入口
├── app.module.ts # 根模块,组装所有子模块
├── user/ # 用户模块
├── auth/ # 认证模块
├── roles/ # 角色模块
├── logs/ # 日志模块
├── guards/ # 自定义守卫
├── filters/ # 异常过滤器
├── interceptors/ # 拦截器
├── decorators/ # 自定义装饰器
├── enum/ # 常量枚举
└── utils/ # 工具函数每个模块就像一个小盒子,有自己的职责,也能和其他模块通信。这种组织方式让项目不管多大,结构都是可预期的。
如果一个模块需要被其他多个模块复用,可以把它设为全局模块(@Global()),或者通过 exports 显式暴露出去。这种显式的依赖声明,比隐式的全局变量要健康得多。
学到最多的几个点
1. 依赖注入(DI)
NestJS 的依赖注入是我觉得最优雅的设计之一。不需要自己 new 对象,只需要在构造函数里声明需要什么,框架会自动给你注入:
constructor(private userService: UserService) {}就一行,UserService 的实例就进来了。如果 UserService 又依赖了其他东西,框架会递归地帮你全部解析好。
这种机制带来的好处是:模块之间的耦合度低,测试也方便 —— 测试的时候可以轻松替换成 mock 对象。
2. 装饰器驱动的编程方式
NestJS 大量使用装饰器,开始的时候会觉得"到处都是 @ 符号",看习惯了会发现这是它最方便的地方。
路由、参数提取、验证、权限控制……全都是装饰器。一个路由的处理函数,从上到下的装饰器就是它的完整"说明书":
@Get(':id')
@UseGuards(AuthGuard)
@UsePipes(ValidationPipe)
getUser(@Param('id') id: string) { ... }一眼就能看出来:这是个 GET 请求、需要登录、参数验证、从路径拿 id。
除了用内置装饰器,还可以自定义装饰器,把重复的逻辑封装起来。比如我写了一个 @Serialize(Dto) 装饰器,用来控制接口返回哪些字段,敏感字段不想暴露给前端,通过这个装饰器一键搞定。
3. 请求生命周期(完整流水线)
NestJS 的请求处理是一条完整的流水线,按顺序经过各个环节:
Middleware → Guard → Interceptor → Pipe → Controller → Service → Interceptor → Filter(出异常时)
每个环节各司其职:
- Middleware:最前置的处理,可以做一些通用逻辑,比如请求日志、耗时统计
- Guard:能不能进来?做权限校验,比如检查是否登录、是否有操作权限
- Interceptor:进来前/出去后做点什么,比如打日志、加缓存、序列化响应数据
- Pipe:进来的数据对不对?做参数验证和类型转换
- Filter:出错了怎么返回?统一异常响应格式
理解了这条流水线,写代码的时候就能清楚当前逻辑该放在哪个环节,出 bug 也能快速定位。
4. TypeORM 的数据库操作
数据库用了 TypeORM,最大的感受是:操作数据库像操作对象一样自然。
最常用的几种关系:
- 一对一:用户 ↔ 资料(一个用户只有一个资料页)
- 一对多:用户 ↔ 日志(一个用户有多条操作记录)
- 多对多:用户 ↔ 角色(一个用户可以有多个角色,一个角色也可以有多个用户,通过中间表关联)
常规的增删改查直接用 Repository 的方法就行。复杂查询用 QueryBuilder,写起来和写 SQL 差不多但更安全(参数化查询),支持分组、排序、分页等高级操作。
5. 数据验证与转换
验证:以前写接口的时候,参数校验经常是手写 if-else,又臭又长。NestJS 搭配 class-validator,用装饰器声明规则,简洁很多:
@IsString()
@IsNotEmpty()
@Length(2, 20)
username: string;配合全局注册的 ValidationPipe,请求进来的时候自动校验,不合法直接返回 400,Controller 里拿到的数据一定是合法的。
转换:用 class-transformer 配合拦截器,可以控制接口返回的数据结构。比如把 User 实体转成 UserDto,只暴露前端需要的字段,密码、加密盐这些敏感字段自动过滤掉。这在写 API 的时候非常实用。
6. 认证与授权 (JWT)
用户认证用的是 JWT(JSON Web Token)方案。流程大概是:
- 用户登录,服务端验证用户名密码
- 验证通过,生成一个加密的 token 返回给前端
- 前端后续请求带上这个 token
- 服务端通过 Guards 校验 token,确定用户身份
密码不能明文存数据库,我用了第三方的包做加盐哈希处理(bcrypt),就算数据库泄露了,密码也是安全的。
权限控制方面还学了 CASL 这个库,可以定义细粒度的权限规则,比如"普通用户只能看自己的文章,管理员可以删任何文章",配合自定义守卫来实现。
7. 环境配置管理
用 @nestjs/config 配合 .env 文件做环境配置。不同环境用不同的文件:
.env.development # 开发环境
.env.production # 生产环境
.env.test # 测试环境
.env # 公共默认配置数据库连接信息、JWT 密钥、端口号这些都放在环境变量里,不写死在代码中。还用了 Joi 做配置验证,启动时如果缺少必要的配置项,程序会直接报错,避免带着错误配置上线。
8. 日志系统
用 nestjs-pino 做日志记录,配置了双输出:
- 控制台:彩色格式化输出,开发时看着清晰
- 文件:按天轮转,每个文件有大小限制,自动归档
日志记录了每次请求的路径、方法、参数、耗时等信息,线上排查问题的时候很有帮助。
9. 测试(Jest)
写了单元测试和端到端(e2e)测试。
单元测试:针对 Service 和 Controller 单独测试,用 mock 替换数据库操作,跑得很快。主要测业务逻辑是否正确。
端到端测试:模拟真实的 HTTP 请求,从路由到数据库全链路验证,确保接口整体功能正常。
用 Jest + Supertest 来写,虽然写测试确实多花一些时间,但改代码的时候有测试兜底,心里会踏实很多。
10. 异常过滤器
没有异常过滤器的时候,每个接口出错返回的格式可能都不一样。前端对接起来很痛苦。
NestJS 的异常过滤器可以把异常处理收拢到一处,统一返回格式:
{
"code": 400,
"message": "参数错误",
"timestamp": "2026-07-26T00:00:00.000Z",
"path": "/api/v1/user",
"method": "POST"
}不管是参数校验失败、数据库报错、还是业务逻辑异常,返回格式都是统一的。前端直接按统一格式处理就行。
写在最后
NestJS 不是那种"学完就能炫技"的框架,它更像一个规规矩矩的框架,提供了写后端应用的一整套方法论。回头看我以前用 Express 写的项目,如果当初用 NestJS,结构应该会清晰很多。
这次学的东西挺多的:模块化架构、依赖注入、TypeORM 数据操作、JWT 认证、数据验证与转换、日志系统、单元测试和 e2e 测试、自定义装饰器和守卫、异常过滤、环境配置管理……每一样在生产中都是很实用的。
框架只是一个工具,但好的工具确实能帮人写出更干净的代码。但这次学习让我最大的收获不是记住了多少 API,而是理解了这套架构思维。未来写任何后端项目,这套思维方式都是通用的。
(其实在学的过程很难受,网上没有很好入门的NestJS课程,我看的这套很不适合小白入门,里面很绕,而且课程很老,不过老师水平确实高,好在硬着头皮学完了,但是说实话不一定记住了常见的API,因为自己代码量不够多。不过,后面肯定有要用到NestJS的时候,到那时候再提高吧)
一步一个脚印,比什么都重要。