NestJS 是什么?一张图看懂企业级后端的骨架

系列第 01 篇 · 从一个 hello 项目出发

零、这篇文章讲什么

我第一次接触 NestJS,花了一个周末把官方 nest new 生成的 hello 项目从头到尾过了一遍。这一篇是我对整个框架的第一版理解:NestJS 到底靠什么把后端代码组织得井井有条的?

阅读这篇文章你会得到三样东西:

  1. 一张 NestJS 的知识架构图(也是整个博客系列的地图)
  2. 四个核心概念(模块化 / 工厂 / 装饰器 / MVC)的直观理解
  3. 一个贯穿全篇的"请求旅程",把四个概念串成一条线

前置知识:会一点 TypeScript 的 class 和装饰器语法就够。不需要后端经验。

一、为什么需要 NestJS

先看痛点。用 Express 这类轻量框架写后端,一开始很爽:

js 复制代码
app.get('/todos', (req, res) => { /* 查列表 */ });
app.get('/todos/:id', (req, res) => { /* 查详情 */ });
app.post('/todos', (req, res) => { /* 新增 */ });

但项目一变大就失控:路由、参数校验、数据库访问、鉴权、日志全堆在 app.js 里。Express 给你自由,但不给你结构------代码怎么写全凭个人风格,换个人就换一套写法。

NestJS 解决的就是这个问题。它的定位可以一句话说清楚:

NestJS 是 Node.js 世界的 Spring Boot。默认 TypeScript、强制分层、内置依赖注入,把"企业级后端该有的结构"用约定固化下来。

特性 Express NestJS
语言 JS 为主 默认 TS,强类型
代码组织 自由,想怎么写怎么写 强制 Controller / Service / Module 分层
依赖注入 没有 内置 DI 容器
团队协作 一人一种风格 约定统一,守住代码质量下限

NestJS 不是另一个"写接口更快的库",而是一套后端工程的纪律。本文要讲的四个核心概念,就是这套纪律的四根柱子。

二、知识架构图

这是我理解 NestJS 的第一版地图,也是本博客系列的总纲:

java 复制代码
             ┌─────────────────────────────────────────────────┐
             │                  main.ts 入口                    │
             │     const app = await NestFactory.create(...)   │
             │              【工厂模式:组装应用】                │
             └───────────────────────┬─────────────────────────┘
                                     │ 读取 @Module 元数据
             ┌───────────────────────▼─────────────────────────┐
             │              AppModule 根模块                    │
             │              【模块化:应用是一棵树】              │
             │    imports / controllers / providers           │
             └───────────────────────┬─────────────────────────┘
                                     │ 递归展开整棵模块树
   ┌─────────────────────────────────┼─────────────────────────────────┐
   │                                 │                                 │
┌──▼───────────────┐         ┌───────▼──────────┐             ┌─────────▼─────────┐
│   Controller 层  │         │    Provider 层    │             │     子模块         │
│   MVC 的 C        │         │    MVC 的业务层   │             │   TodosModule      │
│   AppController  │ ──────► │    AppService     │             │   (imports 引入)   │
│   接请求 / 回响应  │  依赖注入│    数据与业务     │             │                   │
└──────────────────┘         └──────────────────┘             └───────────────────┘
         ▲                                ▲
         │  @Controller() @Get()          │  @Injectable()
         └────────【装饰器模式:贴标签】──────┘

读图顺序(也是本文结构):

  1. 入口NestFactory.create(),一个工厂方法,接收根模块
  2. 模块化 → 工厂读 AppModule 上的元数据,递归展开出整棵模块树
  3. 装饰器 → Controller / Provider / Module 全靠装饰器"贴标签"声明自己是谁
  4. MVC + DI → 请求进来走 Controller → 注入 Provider → 返回响应

下面一节一节拆开讲。

三、核心概念巡礼

1. 模块化:应用是一棵树

NestJS 的最小组织单元是 Module(模块) 。我的 hello 项目里 app.module.ts 是这样的:

ts 复制代码
@Module({
  imports: [TodosModule],       // 引入别的模块(子树)
  controllers: [AppController], // 当前模块的控制器
  providers: [AppService],      // 当前模块的 provider
})
export class AppModule {}

模块化的本质:把"一群接口互相乱调"变成"每个业务一块自留地"。 三个要点:

  • 模块是一棵树,不是一堆AppModule 是树根,imports 声明子树。Nest 启动时从根递归展开,把整棵树的 controller、service 全找出来。谁依赖谁,全写在 imports 里,明明白白。
  • 模块是作用域边界 。一个模块里的 provider 默认只在模块内可见;想给别人用,得主动 exports 出去,别人 imports 进来。没有"全局魔法变量"。
  • 模块可以复用 。一个 TodosModule 可以同时被多个模块引入------这正是 Todo 这类业务模块应该有的样子。

类比:模块就像蜜雪冰城的菜单分区------甜品区、饮品区各自成块。顾客(其他模块)通过菜单(imports)点单,而不是自己翻进后厨拿货。

2. 工厂模式:把组装交给工厂

NestJS 的入口 main.ts 只有三行核心代码:

ts 复制代码
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT ?? 3000);

NestFactory 就是一个工厂类工厂模式解决的问题是:调用方不知道"对象怎么被创建、怎么被管理",只跟工厂打交道。

我自己写过一个最小版本 factory_demo/1.mjs 来理解它:

js 复制代码
class MixueFactory {
  static create(type) {
    switch (type) {
      case "ice":   return new IceCream();
      case "lemon": return new LemonTea();
      case "milk":  return new MilkTea();
    }
  }
}
const drink = MixueFactory.create("ice");
drink.show();   // 冰激凌3元

对照表:

我的 demo NestJS 对应关系
IceCream / LemonTea AppController / AppService 各种类
统一的 show() 接口 统一的构造/注入约定 接口约定
MixueFactory.create(type) NestFactory.create(AppModule) 工厂方法
调用方不关心怎么 new 你从不写 new AppService() 解耦

NestFactory.create() 只是最外层的第一层工厂。它内部还藏着一层更重要的工厂------依赖注入容器,负责创建和管理所有 provider。这层会在系列第 02 篇单独展开。

3. 装饰器模式:给代码贴标签

你第一次看 NestJS 代码时一定会注意到满屏的 @

ts 复制代码
@Controller()                    // 类装饰器:这是一个控制器
export class AppController {
  @Get()                         // 方法装饰器:这是一个 GET 路由
  getHello(): string {
    return this.appService.getHello();
  }
}

装饰器模式在这里的本质是:把"这个类/方法是什么、该怎么处理"的信息,贴在代码本身旁边,而不是写进一张独立的 if-else 路由表里。

对比两种写法:

js 复制代码
// 不用装饰器(Express 式):路由和处理逻辑分开写
app.get('/todos/:id', (req, res) => { /* 处理 */ });
ts 复制代码
// 用装饰器(NestJS 式):路由就在方法正上方,代码即文档
@Get(':id')
findOne(@Param('id') id: string) { /* 处理 */ }

实现原理:@Controller@Get 这些装饰器本质是函数,它们把元数据写进类的反射元数据里。工厂启动时读这些元数据,自动生成路由表。所以 main.ts 里那句注释很准确------装饰器只是描述这个类长什么样子,真正的装配发生在工厂里。

NestJS 把装饰器用到了极致,是一整个家族:

类型 装饰器 作用
类装饰器 @Module @Controller @Injectable 声明类属于哪一类
方法装饰器 @Get @Post @Put @Delete 声明 HTTP 路由
参数装饰器 @Param @Body @Query @Req 声明从哪取参数
能力装饰器 @UseGuards @UsePipes @UseInterceptors 挂守卫 / 校验 / 拦截器

4. MVC 架构:职责分层

MVC(Model-View-Controller)在 NestJS 里的映射:

javascript 复制代码
Controller ──> Service ──> (Model / 数据库)
    │            │
    └────────────┴──> 返回 JSON 给前端(API 后端的"视图")
MVC 概念 NestJS 中的位置 hello 项目里的例子
C 接请求、控制流程 *.controller.ts AppController.getHello()
M 业务逻辑 + 数据 *.service.ts + 数据实体 AppService.getHello()
V 展示层 返回的 JSON(对 API 后端就是响应体) return 'Hello World!'

看 hello 项目里的分工 app.controller.tsapp.service.ts

ts 复制代码
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();   // 控制器不写业务,交给 service
  }
}
ts 复制代码
@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';               // 业务层只处理数据,不认识 HTTP
  }
}

MVC 的核心价值:每一层只能干自己的事。 Controller 只做"翻译"(HTTP 请求 ↔ 方法调用),Service 只处理业务数据、完全不认识 HTTP。这样分层的直接红利是测试容易 :测 Service 不用起服务器,new AppService() 直接调就行。

注意:NestJS 的 View 层通常不是服务端渲染的页面,而是返回 JSON 给前端 SPA。所以它更像一个"去掉了视图模板的 MVC"------Controller + Service(业务层)+ 数据模型。

四、串起来:一次请求的完整旅程

用 hello 项目走一遍全流程,四个概念就全通了:

java 复制代码
1. 启动:NestFactory.create(AppModule)
     └─ 工厂按 @Module 元数据展开模块树
     └─ 读到 AppController 上有 @Get() → 生成路由表:GET / → getHello
     └─ 读到构造器需要 AppService → 容器 new AppService() 并注入

2. 请求:浏览器访问 http://localhost:3000/
     └─ Nest 查路由表 → 命中 AppController.getHello()
     └─ getHello() 调用 this.appService.getHello()   ← 注入好的实例
     └─ AppService 返回 'Hello World!'               ← 业务层干活
     └─ Controller 把结果作为响应体返回               ← 控制层收尾

六步里四个概念全部上场:模块化提供容器结构,工厂负责装配,装饰器生成路由表,MVC 决定每层职责。 这就是 NestJS 的设计------它不是一个新语言,而是把这套被验证过的企业级模式搬到了 Node.js 上。

五、NestJS 的生态:入门之后还有这些

知道框架能做什么,才知道现在学的每个概念未来长在哪。入门之外,NestJS 内置了大量开箱即用的企业级能力(都是装饰器 + 注入的延伸):

  • 管道 Pipe :参数校验(配合 class-validator),非法输入直接挡在路由外
  • 守卫 Guard:权限/登录拦截,挂在路由上
  • 异常层 :内置 NotFoundException 等一整套标准异常类,统一 JSON 输出
  • 数据库@nestjs/typeorm / @nestjs/mongoose,ORM 也变成可注入的 provider
  • 微服务:同一套 Service 可同时暴露 HTTP / TCP / gRPC / MQ
  • 测试*.spec.ts,DI 让单元测试非常自然

六、学习路线图(系列地图)

我把 NestJS 的学习拆成一条主线,文章会随项目进度一篇篇补上:

kotlin 复制代码
NestJS 学习主线(banckend/nestjs/)
├── ✅ 01 NestJS 是什么:一张图看懂企业级后端的骨架   ← 本篇
├── ✅ 02 依赖注入:从"你要什么"到"容器给你"
├── ⬜ 03 路由与参数:@Get @Body @Param @Query    (hello/src/todos 已有素材)
├── ⬜ 04 管道与校验:class-validator 防御非法输入
├── ⬜ 05 数据库:TypeORM 持久化
├── ⬜ 06 守卫与鉴权:保护你的接口
└── ⬜ 07 模块作用域:@Global 与 provider 生命周期

每篇配套的练习代码都在 hello/(或新的子项目)里,写完一篇更新一次本文的路线图。

小结

四个概念一句话记忆:

概念 一句话 代码证据
模块化 把后端按业务切成树状的可复用模块 imports: [TodosModule]
工厂模式 你不 new 对象,工厂帮你 new 并管理 NestFactory.create(AppModule)
装饰器模式 给类/方法贴标签,启动时读标签装配 @Controller @Get @Module
MVC 架构 Controller 接请求、Service 干业务、JSON 当视图 app.controller.ts → app.service.ts
相关推荐
liuxiaocheng1 小时前
文本生成的进阶:generateText / streamText 里迟早会撞上的东西
前端·后端·ai编程
嘟嘟07171 小时前
NestJS 入门:从 NestFactory 入口到 Module/Controller/Service 模块化结构一次讲清
typescript·node.js·nestjs
江小渔1 小时前
训练工程与训练平台入门33 追踪一次 TrainJob 的完整生命周期
后端
无糖可可果1 小时前
NestJS 学习分享:从工厂模式到企业级后端开发
后端
vipbic2 小时前
网站升级了,我却有点舍不得
前端·vue.js·后端
IT_陈寒2 小时前
Java Stream并行处理让我数据库崩了两次
前端·人工智能·后端
2601_963870222 小时前
【计算机毕业设计】基于Spring Boot的考研信息交流平台的设计与实现
java·spring boot·后端
大模型丫丫2 小时前
Spring Boot 入门指南:从零开始构建你的第一个应用
java·spring boot·后端
卷无止境2 小时前
FastAPI的测试事件在测什么?
后端·python·fastapi