大家好 👋,我是 Moment,目前正在使用 Next.js、NestJS、LangChain 开发 DocFlow。这是一个面向 AI 场景的协同文档平台,集成了基于
Tiptap的富文本编辑、NestJS后端服务、实时协作与智能化工作流等核心模块。在这个项目的持续打磨过程中,我积累了不少实战经验,不只是
Tiptap的深度定制、编辑器性能优化和协同方案设计,也包括前端工程化建设、React 源码理解以及复杂项目架构实践。如果你对 AI 全栈开发、文档编辑器、前端工程化或者 React 源码相关内容感兴趣,欢迎添加我的微信
yunmz777一起交流。觉得项目还不错的话,也欢迎给 DocFlow 点个 star ⭐

在上一节里,我们已经知道为什么很多 Node.js 项目做到后面会越来越难维护,也知道 NestJS 的价值不在于接口写得有多快,而在于系统能不能组织得稳。
这一节要做的事,是先建立对 NestJS 的整体认知。
很多人第一次接触 NestJS 时,脑子里会同时冒出很多词:Controller、Service、Module、Provider、依赖注入、装饰器、Pipe、Guard、Interceptor、异常过滤器。名词一多,就很容易陷入一种感觉,像是每个词都认识,但不知道它们之间到底是什么关系。
所以这一篇不急着写代码,而是先把几个核心问题讲清楚:
NestJS到底是什么- 它为什么不是简单的
Express封装 - 它的整体结构为什么会让人想到
Angular - 一个请求进入系统之后,大致会经过哪些环节
- 常见核心概念在系统里各自扮演什么角色
当这张地图在心里搭起来之后,后面再学具体语法,就不会只是在背装饰器。
NestJS 不是简单的 Express 封装
很多初学者第一次看到 NestJS,会下意识地把它理解成在 Express 上包了一层更复杂的写法。这种理解不算完全错,但远远不够。
NestJS 的确可以运行在 Express 之上,也可以切换到底层适配器,比如 Fastify。可它和单纯封装一个路由库有本质区别。
如果只是简单封装,框架通常只是在原有能力外面包一层更方便的 API。你依然需要自己决定目录怎么划分、模块怎么组织、依赖怎么注入、公共逻辑怎么统一处理。
NestJS 做得更多。它在 HTTP 服务能力之上,进一步给出了一整套组织规则:
- 用模块组织功能边界
- 用控制器承接请求入口
- 用服务层承载业务逻辑
- 用依赖注入管理对象关系
- 用统一机制处理校验、权限、日志、异常这类横切问题
所以你不能把 NestJS 只看成路由层换了一种写法。更准确地说,它是在底层 Web 框架之上,加了一层更完整的应用架构。
也正因为如此,NestJS 的学习重点从来不只是会不会写 @Get()、@Post()。真正重要的是,你要开始理解一个后端系统是如何被拆分、组合和约束起来的。
它是一个有明确架构约束的服务端框架
NestJS 最大的特点之一,是它愿意在一开始就给你更多结构约束。
这里说的约束,不是限制你写业务,而是帮助你避免项目过早滑向混乱。
在没有清晰结构的项目里,最容易发生的事情是,大家各写各的。有人把业务写在路由里,有人把数据库操作塞进工具函数,有人把异常处理散在每个接口里。短期看没问题,长期就会让系统越来越难接手。
NestJS 的思路刚好相反。它希望你从项目一开始就把这些问题想清楚:
- 功能边界怎么切
- 依赖关系怎么管理
- 公共逻辑放在哪一层
- 请求进入系统后由谁接、谁处理、谁返回
因此,NestJS 不是那种先随便写、后面再整理的框架,而是更鼓励你在初期就建立有秩序的结构。
这种架构约束带来的直接结果是,项目会更像一个被设计过的系统,而不是一堆能跑的接口集合。
当然,这也意味着它比纯底层框架更有学习成本。你需要理解模块、依赖注入、生命周期、横切逻辑这些概念。可一旦跨过这个阶段,你会发现后续很多能力都能自然挂在这个骨架上。
为什么很多人说它像 Angular
很多人第一次学 NestJS 时,都会听到一句评价,说它很像 Angular。这不是说它们一个写前端、一个写后端却在功能上相似,而是说它们在组织思想上有明显共通点。
这种相似主要体现在几个方面:
- 都强调模块化
- 都使用依赖注入管理对象关系
- 都大量使用装饰器表达元信息
- 都强调清晰分层和明确职责
- 都希望开发者在一个统一约定下组织项目
如果你有 Angular 经验,再看 NestJS,通常会觉得很亲切。因为你会发现很多概念迁移成本很低。比如模块、提供者、依赖注入、装饰器这些思路,理解路径是连贯的。
即便你没有学过 Angular 也没关系。这个类比最重要的作用,是帮助你意识到,NestJS 不是一个只关心 HTTP 请求收发的轻量库,它更像一个完整的应用框架。
也就是说,它关心的不只是接口怎么响应,还关心应用如何分层、对象如何协作、公共能力如何接入、系统如何长期维护。
NestJS 的几个核心关键词
第一次认识 NestJS,有几个关键词一定要先建立印象。后面很多内容,都会围绕这些词展开。
模块化
模块化的核心作用,是把系统按功能边界切开。
比如用户、订单、支付、通知,这些通常都可以拆成不同模块。每个模块负责自己的控制器、服务、依赖和对外暴露能力。这样做的好处是,项目不会随着功能增加而变成一锅粥。
依赖注入
依赖注入的核心作用,是把对象怎么创建、怎么互相拿到这件事从业务代码里抽离出来。
你不需要在一个类里到处 new 另一个类,而是把依赖声明出来,由框架负责提供。这样代码更容易测试,也更容易替换实现。
装饰器
装饰器是 NestJS 用来描述结构和行为的一种方式。
比如 @Controller() 表示这是控制器,@Injectable() 表示这个类可以被注入,@Get() 表示它对应某个 GET 路由。装饰器让代码在视觉上更接近声明式写法,也让框架能拿到更多元信息来完成自动组装。
AOP 思路
这里说的 AOP,可以先简单理解成把公共逻辑从业务代码里抽出来,在统一位置处理。
比如日志记录、权限校验、参数转换、响应包装、异常处理,这些逻辑往往会反复出现在很多接口里。NestJS 通过 Pipe、Guard、Interceptor、异常过滤器等机制,把这些横切逻辑放到更合适的位置,而不是散落在每个接口里重复书写。
约定优于配置
NestJS 不是完全不允许配置,而是更强调在一套清晰约定下开发。
比如你会默认看到 Controller 负责入口,Service 负责业务,Module 负责组织,Provider 负责被注入能力。这样做的好处是,团队成员看到一个陌生项目时,更容易快速建立认知,不需要先猜作者的个人习惯。
一个请求从哪里进来
理解 NestJS,最好的方式之一,就是顺着一次请求往里看。
当客户端发来一个 HTTP 请求时,它并不是直接打到某个业务函数上,而是会先经过框架组织好的请求处理链路。你可以先建立一个粗略印象:

看图时不必一次记全细节,但可以先抓住一点,NestJS 处理请求不是请求一进控制器就立刻返回这么简单,它把校验、权限、拦截、异常处理这些公共逻辑明确插进了请求生命周期。
这正是它和很多只负责把请求分发到处理函数的框架最大的不同之一。
Controller、Service、Module、Provider 分别是什么
这几个概念是 NestJS 最基础的骨架。只要先把它们的位置搞清楚,后面很多内容都会容易很多。
Controller
Controller 可以理解成请求入口层。
它负责接收请求、读取参数、调用业务逻辑,并把结果返回出去。它应该尽量保持薄一些,重点是接请求、转调业务,而不是把复杂业务堆在里面。
Service
Service 主要承载业务逻辑。
比如订单如何创建、库存如何扣减、用户状态如何校验,这些更偏业务规则的内容,通常应该放在 Service 里。这样控制器不会变得臃肿,业务也更容易复用和测试。
Module
Module 负责组织功能边界。
它会把一组相关的控制器、服务、提供者组织在一起,并决定哪些能力在模块内部使用,哪些能力可以导出给其他模块。可以把它理解成应用结构层面的分区机制。
Provider
Provider 是一个更通用的概念。
只要某个类或值可以被框架管理并注入到别处使用,它都可以归入 Provider 的范畴。很多 Service 本质上就是一种 Provider,只是因为职责偏业务,所以我们日常更常叫它服务。
如果用一句更直白的话总结:
Controller负责接请求Service负责写业务Module负责做组织Provider负责被注入和复用
Pipe、Guard、Interceptor、Filter 在链路中的位置
这四个概念是很多初学者最容易混在一起的地方。它们都作用在请求链路上,但解决的问题并不一样。
Pipe
Pipe 更关注参数进入业务之前的处理。
它常用于参数转换和数据校验。比如把字符串转成数字,或者检查请求体是否满足某个 DTO 结构。你可以把它理解成参数进控制器之前的整理和把关。
Guard
Guard 更关注这个请求有没有资格继续往下走。
它常用于登录校验、角色权限、访问控制这类判断。换句话说,它回答的是当前调用方有没有资格继续访问这段逻辑。
Interceptor
Interceptor 更像一个包裹在业务执行前后的拦截层。
它既可以在调用前做一些事情,也可以在调用后统一处理结果。比如记录耗时、统一包装响应结构、做缓存、埋点等,都很适合放在这里。
Filter
异常过滤器也就是 Filter,它主要负责兜底异常处理。
当链路中的某一步抛出异常时,Filter 可以统一接住它,再决定最终如何返回给客户端。这样你就不用在每个接口里自己写重复的 try-catch。
如果只记一句区分方法,可以先这样理解:
Pipe管参数Guard管放行Interceptor管前后拦截Filter管异常兜底
第一次接触时应该怎样建立整体认知
学习 NestJS 最怕一上来就陷进零散语法里。今天看一个装饰器,明天背一个生命周期,后天又去记某个命令,最后很容易学得支离破碎。
更好的方式是,先抓住这条主线:
NestJS是一个强调工程组织的后端框架- 它通过模块化和依赖注入组织系统结构
- 它通过控制器和服务拆分入口层与业务层
- 它通过
Pipe、Guard、Interceptor、Filter管理公共横切逻辑 - 它的目标不是只把接口写出来,而是让系统在增长后依然保持清晰
你可以先不急着把所有名词都背下来,但一定要先知道每个概念大概在什么位置、解决什么问题。因为只要位置感建立起来,后面每学一个新概念,都会自然挂回这张地图里。
小结
认识 NestJS,本质上不是记住一堆新名词,而是先理解它背后的组织思想。
它不是简单的 Express 封装,也不只是多写几层类。它更像是在 Node.js 服务端开发里,提前帮你搭好一套清晰的应用骨架,让请求入口、业务逻辑、模块边界、依赖关系和公共横切逻辑都能有更明确的落点。
如果你先从这个角度去看 NestJS,后面再学习 Controller、Service、Module、Provider 以及整条请求生命周期,就会顺畅很多。
接下来会进入更具体的部分,看一个 NestJS 项目从初始化到基本运行,通常怎样搭起来。