《NestJS 从入门到资深》书稿(Markdown)

《NestJS 从入门到资深》书稿(Markdown)

作者:SOLO(与你协作完成) 版本:v0.1(目录 + 入门篇前 3 章正文)

说明:你选择的是"扩展版",完整成书会非常长(15--20 万字级别)。我先交付可直接写下去的整本书结构 ,并把入门篇最关键的前 3 章写成完整正文(含练习与检查清单)。接下来你只要告诉我"继续写第 X 章",我会按本书风格把该章补齐为完整正文。


0. 这本书适合谁?

本书面向三类读者(你也可以跳读对应路线):

  1. 完全新手 :Node.js / TypeScript 都不熟
    • 建议:先看 附录 A(TypeScript 速成)+ 附录 B(Node.js/HTTP 速成),再从第 1 章开始。
  2. 会 Node/TS,不会 NestJS (最常见)
    • 建议:从第 1 章开始,跟着示例项目一路做到上线。
  3. 已有 NestJS 经验,想进阶/资深
    • 建议:入门篇快速扫读,重点看进阶篇(架构、性能、安全、测试)和资深篇(DDD、微服务、可观测性、部署、组织级工程实践)。

1. 你将做出什么?

1.1 旗舰示例项目(贯穿全书)

我们用一个更贴近企业真实场景的 SaaS 后台系统 贯穿全书:

项目名:AcmeHub(多租户项目协作平台)

  • 多租户(tenant)隔离:按租户隔离数据、配置、权限
  • 用户体系:注册/登录、JWT、刷新令牌、RBAC
  • 核心业务:项目(Project)、任务(Task)、评论(Comment)、通知(Notification)
  • 工程化:配置分层、日志、链路追踪、错误监控、测试金字塔
  • 数据层:PostgreSQL(主),Redis(缓存/限流),可选 MQ(异步任务)
  • 部署:Docker Compose → Kubernetes(可选)

1.2 补充示例(按章节"支线任务"出现)

为满足"多业务场景都有"的需求,本书在对应章节加入三组可独立运行的支线模块:

  • 电商/订单:订单状态机、幂等、支付回调、库存扣减、延迟关单
  • 内容/社区:帖子/评论/关注、反作弊、敏感词、审计日志
  • 通用脚手架:企业后端模板(分层、规范、代码生成、CI)

2. 本书约定(重要)

2.1 代码约定

  • TypeScript 严格模式(strict)
  • DTO + 校验(class-validator)是默认写法
  • 所有对外输入都要:校验、转换、白名单、错误可观测
  • 统一异常格式(HTTP/业务错误码)
  • 模块边界清晰:控制器(Controller)只做协议适配;业务在 Service;复杂业务抽 Domain/UseCase(进阶篇)

2.2 学习方式

每章结构固定为:

  • 本章目标
  • 核心知识
  • 项目落地(AcmeHub)
  • 常见坑
  • 练习题
  • 检查清单(Checklist)

3. 全书目录(入门 → 进阶 → 资深)

目录是扩展版深度;如果你希望精简,也可以告诉我删减哪些章节。

第一篇:入门(能写、能跑、能做出 API)

  1. NestJS 是什么:为什么是它?
  2. 第一个 NestJS 应用:项目结构与开发体验
  3. 依赖注入与模块系统:Controller / Provider / Module
  4. DTO 与数据校验:Pipe、ValidationPipe、class-validator
  5. 异常处理与统一返回:Exception Filter + 错误码体系
  6. 配置管理:ConfigModule、环境变量、配置分层
  7. 日志:从 console 到结构化日志(Pino/Winston)与请求追踪
  8. API 文档:Swagger(OpenAPI)与"可维护的接口契约"
  9. 数据库入门:ORM 选择与迁移(TypeORM/Prisma 选型与落地)
  10. CRUD 实战:用户与项目模块(AcmeHub v1)

第二篇:进阶(像"工程"一样写后端)

  1. 架构分层:Controller-Service-Repository 的边界
  2. 认证与授权(上):JWT、Refresh Token、注销与会话策略
  3. 认证与授权(下):RBAC、ABAC、资源级权限与审计
  4. 事务与一致性:数据库事务、领域事件、最终一致性入门
  5. 缓存:Redis、Cache-Aside、热点与雪崩、缓存键规范
  6. 幂等与并发控制:请求幂等、分布式锁、乐观锁/悲观锁
  7. 消息队列与异步任务:BullMQ/Redis、重试、死信与延迟任务
  8. 文件上传与对象存储:本地/MinIO/S3、断点续传思路
  9. WebSocket 与实时系统:通知、在线状态与房间管理
  10. 测试(上):单元测试(Jest)、依赖注入下的可测设计
  11. 测试(下):集成测试、e2e、Testcontainers 思路
  12. 性能(上):Node 事件循环、阻塞点、Profiling 基础
  13. 性能(下):分页/游标、N+1、批量、索引与慢查询治理
  14. 安全(上):输入校验、CORS、CSRF、XSS、SSRF、注入与反序列化
  15. 安全(下):限流、风控、密钥管理、依赖安全与供应链
  16. 可观测性(上):日志、指标、追踪(OpenTelemetry)
  17. 可观测性(下):告警、SLO、故障演练与线上排障套路

第三篇:资深(架构演进、系统设计与组织级实践)

  1. 领域建模与 DDD:从贫血模型到领域服务
  2. 模块化单体:边界清晰的"可演进单体"
  3. 微服务拆分策略:不要为了微服务而微服务
  4. 通信:REST vs gRPC vs MQ,契约管理与版本治理
  5. 数据拆分与一致性:分库分表、Saga、Outbox、CQRS 入门
  6. 多租户(进阶):隔离模型(库/Schema/行级)、迁移与运维
  7. 网关与 BFF:统一鉴权、聚合、灰度、限流与熔断
  8. 配置中心与特性开关:灰度发布、A/B、回滚策略
  9. 部署(上):Docker、Compose、环境一致性
  10. 部署(下):Kubernetes、Helm、滚动更新与资源治理
  11. CI/CD:流水线、质量门禁、自动化回归与发布规范
  12. 代码规范与工程化:Monorepo、模块边界、依赖规则、生成器
  13. 组织级实践:技术债、架构评审、事故复盘、带团队的后端方法论

附录

A. TypeScript 速成:你需要的那 20%(面向 NestJS) B. Node.js/HTTP 速成:请求生命周期与常用概念 C. SQL 必备:索引、事务隔离级别、锁与慢查询 D. 常用中间件:Nginx、Redis、PostgreSQL、MinIO 快速上手 E. 面试题与系统设计题:从初级到资深的题库与解法


第一篇:入门

第 1 章 NestJS 是什么:为什么是它?

本章目标

读完本章你将能回答:

  1. NestJS 解决了 Node 后端哪些"长期痛点"?
  2. NestJS 的核心设计哲学是什么?
  3. NestJS 适合哪些项目,不适合哪些项目?

1.1 Node 后端常见痛点

很多团队从 Express/Koa 起步很快,但随着业务增长会遇到:

  • 缺少结构约束:路由、控制器、业务逻辑、数据访问混在一起
  • 依赖管理混乱:手写 new、手动组装对象,难测、难替换、难重构
  • 横切关注点散落:鉴权、日志、校验、异常格式、监控到处重复
  • 大型项目协作困难:模块边界模糊,新人难上手

NestJS 的价值不是"让你写出一个 Hello World",而是让你在项目变大后仍然: 可维护、可测试、可协作、可演进。

1.2 NestJS 的核心:模块化 + 依赖注入 + 装饰器

NestJS 借鉴了后端成熟生态的经验(尤其是 Java/Spring 的工程化思想),在 Node/TS 世界给出一套相对完整的工程框架:

  • 模块(Module):把业务能力封装成可组合的模块
  • 依赖注入(DI):把"创建依赖"交给框架容器,你只描述"我需要什么"
  • 装饰器(Decorator):用声明式方式描述路由、参数、校验、守卫等
  • 统一的扩展点:Pipe / Guard / Interceptor / Filter,让横切能力标准化

1.3 什么时候选 NestJS?

适合:

  • 中大型后端项目(多人协作、长期演进)
  • 对测试、规范、可观测性有要求的团队
  • 想要快速搭建"企业级后端骨架"的项目

不太适合(至少要谨慎):

  • 极小型脚本或一次性项目(工程化成本可能偏高)
  • 团队完全不接受强约束(例如讨厌装饰器/DI 的心智负担)

1.4 本书的"工程目标"

本书不仅教 API 怎么写,更要把你带到"资深后端的视角":

  • 如何设计模块边界
  • 如何让系统可测、可观测、可部署
  • 如何应对一致性、并发、性能与安全

练习题

  1. 你当前团队/项目最痛的 3 个问题是什么?它们能否用"模块化 + DI + 统一扩展点"缓解?
  2. 你觉得"强约束"对团队是利大于弊还是弊大于利?为什么?

Checklist(本章检查清单)

  • 我能用自己的话解释 NestJS 解决的痛点
  • 我能说清楚 Module/DI/Decorator 三者的关系
  • 我知道 NestJS 适用/不适用的场景

第 2 章 第一个 NestJS 应用:项目结构与开发体验

本章目标

  • 跑起来一个 NestJS 项目
  • 理解 Nest 项目的基本结构与请求流转
  • 知道如何启用常用开发能力(热更新、环境变量、基础配置)

2.1 创建项目(两种方式)

方式 A:使用 Nest CLI(推荐)

  • 优点:脚手架与生成器体验最好
  • 缺点:需要安装 CLI

方式 B:自己搭建(适合已有工程体系)

  • 优点:可控、可与 Monorepo/自定义构建整合
  • 缺点:需要你对 TS/构建更熟

本书不强制你使用哪种方式。你只需要最终得到一个能 start:dev 的 Nest 项目即可。

2.2 典型目录结构说明

你会经常看到类似结构:

text 复制代码
src/
  main.ts              # 应用入口:创建 Nest 应用、注册全局中间件等
  app.module.ts        # 根模块:聚合所有业务模块
  app.controller.ts    # 示例控制器(通常会被你删除或改造)
  app.service.ts       # 示例服务
test/
  app.e2e-spec.ts

2.3 请求在 Nest 中怎么走?

简化后的请求链路:

  1. 请求进入(可能先经过 Middlewares)
  2. 进入路由匹配到 Controller 的某个方法
  3. 在进入方法前,可能经过:
    • Guard(鉴权/权限)
    • Interceptor(拦截:日志、缓存、超时、响应映射)
    • Pipe(参数校验与转换)
  4. Controller 调用 Service(业务逻辑)
  5. 发生异常 → Exception Filter 统一处理
  6. 返回响应

先记住一句话:
Controller 负责"协议适配",Service 负责"业务逻辑"。

2.4 main.ts:你最常改的文件之一

main.ts 通常做:

  • 全局 ValidationPipe
  • 全局异常过滤器
  • 全局前缀(如 /api
  • Swagger 初始化
  • 启动端口与监听

下面是一个"书中推荐的 main.ts 形态"(先理解,不要求一次写全):

ts 复制代码
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.setGlobalPrefix('api');

  app.useGlobalPipes(new ValidationPipe({
    whitelist: true,            // 移除 DTO 未声明字段
    forbidNonWhitelisted: true, // DTO 外字段直接报错(更安全)
    transform: true,            // 自动把入参转成 DTO 类型
  }));

  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

whitelist/forbidNonWhitelisted/transform 是"企业级默认值"。你会在第 4 章深入理解它们。

2.5 "AcmeHub"项目:我们从什么开始?

入门篇我们会先把 AcmeHub 做到:

  • 能跑
  • 有用户与项目两个模块
  • 有基础的校验、异常格式、Swagger
  • 能连上数据库并完成 CRUD

常见坑

  1. 忘了开启 transform:导致 DTO 类型不生效(例如数字字符串没转成 number)
  2. 没有 forbidNonWhitelisted:前端多传字段被悄悄忽略,安全与可观测性变差
  3. Controller 里写业务:短期快,长期重构成本巨大

练习题

  1. 解释 ValidationPipe 三个关键选项:whitelistforbidNonWhitelistedtransform 各解决什么问题?
  2. 画出请求从进入到返回的链路,标注 Guard/Pipe/Interceptor/Filter 在哪里生效。

Checklist

  • 我知道 main.ts 的职责
  • 我理解 Nest 的请求链路中各扩展点的位置
  • 我能说清楚为什么 Controller 不该塞业务逻辑

第 3 章 依赖注入与模块系统:Controller / Provider / Module

本章目标

  • 理解"依赖注入"是什么,为什么对可测试/可维护重要
  • 能写出一个模块:Controller 调 Service,Service 作为 Provider 注入
  • 理解 Provider 的作用域与导入/导出(exports)

3.1 先把话说清:什么是依赖注入(DI)?

不使用 DI 的写法(伪代码):

ts 复制代码
class UserService {
  private repo = new UserRepository(); // 自己 new
}

问题:

  • UserService 依赖了 UserRepository 的"具体实现",替换困难
  • 测试时想换成 FakeRepo/MockRepo,不好做
  • 当依赖越来越多,构造与生命周期管理会变成灾难

使用 DI 的思想:

  • 你只声明"我需要一个 IUserRepository"
  • 容器负责"提供一个实现"

NestJS 通过 Provider + 注入容器来管理这些对象。

3.2 Controller / Provider / Module 之间的关系

三句话记住:

  1. Controller:对外提供 HTTP 接口
  2. Provider(通常是 Service):承载业务能力,可注入、可复用、可替换
  3. Module:把一组 Controller 与 Provider 组织在一起,并声明依赖关系

示例(简化):

ts 复制代码
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';

@Module({
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

3.3 一个最小可用的"用户模块"

3.3.1 user.service.ts
ts 复制代码
import { Injectable } from '@nestjs/common';

@Injectable()
export class UserService {
  findById(id: string) {
    return { id, name: 'demo' };
  }
}

@Injectable() 的意义:

  • 告诉 Nest:这是一个可被容器管理的 Provider
  • 容器可以创建它并注入它的依赖(如果它构造函数里声明了依赖)
3.3.2 user.controller.ts
ts 复制代码
import { Controller, Get, Param } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get(':id')
  getUser(@Param('id') id: string) {
    return this.userService.findById(id);
  }
}

这里发生了什么?

  • Controller 的构造函数声明了依赖 UserService
  • Nest 容器会在运行时把 UserService 实例注入进来
3.3.3 user.module.ts
ts 复制代码
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';

@Module({
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

然后在 app.module.ts 引入即可:

ts 复制代码
import { Module } from '@nestjs/common';
import { UserModule } from './user/user.module';

@Module({
  imports: [UserModule],
})
export class AppModule {}

3.4 Provider 的导入与导出:exports

当你希望 别的模块也能注入你的 Provider 时,需要:

  • 在提供者所在模块 providers 里声明它
  • 并在 exports 导出它
ts 复制代码
@Module({
  providers: [UserService],
  exports: [UserService],
})
export class UserModule {}

然后其他模块 import UserModule,就可以注入 UserService。

经验:能不导出就不导出。导出意味着"对外承诺",会增加耦合。进阶篇我们会讲如何设计模块边界。

3.5 常见坑与排查

  1. Nest 无法解析依赖(Cannot resolve dependencies)
    多半是:没在 providers 声明、没在 exports 导出、没在 imports 引入。
  2. 循环依赖(Circular dependency)
    初学时最常见。解决方式包括:重新划分模块边界、抽取公共模块、forwardRef(但不要滥用)。
  3. 把"配置/常量"当类注入
    需要使用自定义 token(进阶篇会系统讲)。

练习题

  1. 用自己的话解释:为什么"自己 new 依赖"会让测试更难写?
  2. 写一个 ProjectModule,包含 ProjectControllerProjectService,实现 GET /projects/:id
  3. ProjectService 依赖 UserService,并解释你需要做哪些 imports/exports 才能注入成功。

Checklist

  • 我知道 Controller/Provider/Module 的职责边界
  • 我能写出一个可注入的 Service 并在 Controller 中使用
  • 我理解 exports 的意义,以及它为什么会带来耦合

接下来写什么?

你可以直接对我说:

  • "继续写第 4 章(DTO 与数据校验)"
  • 或者"先把进阶篇第 12 章(JWT 与刷新令牌)写出来"

我会按本书风格补齐为完整正文,并持续更新这份 Markdown 书稿文件。

相关推荐
Memory_荒年2 小时前
Java + FFmpeg:从“玩具”到“工业级”的音视频实战
后端
awljwlj3 小时前
黑马点评复习—缓存相关【包含可能的问题和基础知识复习】
java·后端·spring·缓存
XY_墨莲伊3 小时前
【实战项目】基于B/S结构Flask+Folium技术的出租车轨迹可视化分析系统(文末含完整源代码)
开发语言·后端·python·算法·机器学习·flask
神奇小汤圆3 小时前
为什么Claude Code这么强?我从泄漏的源码里挖到了核心秘密
后端
精品源码屋3 小时前
千万级CSV/Excel表统计教程:基于本地数据库的自然语言单表、多表分析 | DT-Bot工作流
后端
Gopher_HBo3 小时前
CompletableFuture运用原理
java·后端
Leinwin3 小时前
GPT-6 API接入完全指南:Symphony架构下的多模态调用与最佳实践
后端·python·flask
snakeshe10104 小时前
Java Web 应用部署实战:从单机到分布式的三种方式
后端