关于 nestjs-dev-guidelines
为 NestJS 及采用 Nest 风格的 Node.js 后端提供开发规范,适用于代码编写、审查与架构决策。内容涵盖安全、模块边界、验证和测试,并强调小范围修改与验证结果。
主要功能
- 01执行纪律与决策树
- 02DTO 输入验证规范
- 03模块边界与数据库规范
- 04响应、分页与日志约定
- 05安全、多租户与测试清单
适用场景
- 01设计或扩展 NestJS 模块
- 02审查后端代码或 PR
- 03处理验证、授权和数据隔离
- 04规划测试与代码修改范围
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ myatminlu/ vector-skills --skill nestjs-dev-guidelines安装后在 Claude Code 中输入/nestjs-dev-guidelines确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonresponses.jsonevals/scripts/grade.pyreferences/00-execution-discipline.md01-folder-structure.md02-naming-conventions.md03-module-design.md04-code-quality.md05-thinking-decision-trees.md06-api-design.md07-standard-responses.md08-pagination-filters-sorting.md09-validation.md10-error-handling.md11-security.md12-authentication-patterns.md13-database-design.md14-database-orm-patterns.md15-migrations.md16-cascade-rules.md17-pipelines-interceptors-guards.md18-events.md19-background-jobs.md20-configuration.md21-logging.md22-observability.md23-testing.md24-performance.md24a-caching-patterns.md25-documentation-swagger.md详细介绍
NestJS 开发指南
一套完整的生产级 NestJS 和 Nest 风格 Node.js 后端规范。当处理已采用 NestJS 概念(如模块、控制器、提供者/服务、DTO、管道和守卫)的代码库,或任务明确涉及设计这些模式时,请遵循这些规则。像资深后端工程师一样思考:一致性胜过巧妙设计,明确胜过隐式,边界胜过捷径。
如何使用本指南
- 编写或审查代码之前,先阅读
references/00-execution-discipline.md和下方的不可妥协事项。 - 对于文件放置、模块边界或重构决策,请使用决策树。
- 如需深入了解某个主题,请打开对应的
references/NN-<topic>.md文件。每个文件都自成一体,包含概要、规则、正反示例、反模式和审查清单。 - 审查 PR 时,请结合相关主题参考资料,逐项检查
references/29-code-review-checklist.md。
适用范围与优先级
当代码库属于“后端 TypeScript”,但不明显是标准 NestJS 应用时,请参考本节。
- NestJS 优先适用。 如果代码库已使用 NestJS 基础构件(
@Module、控制器、提供者/服务、守卫、管道、DTO),或用户要求设计 NestJS 解决方案,请直接应用本指南。 - 纯 Node 后端仅部分适用。 如果代码库使用纯 Express/Fastify/Hono/Koa,且不包含 NestJS 基础构件,则仅采用本指南中的跨领域指导(验证、契约、授权、数据库设计、测试、可观测性)。不要将 Nest 专属 API、装饰器或文件夹结构强加给该代码库。
- 先保障结果,再考虑工具。 下方的不可妥协事项首先是必须遵守的原则,其次才是实现默认方案。集中式输入验证、稳定的响应契约、经过结构化且脱敏处理的日志、所有权检查和测试覆盖,比实现这些目标时使用的具体库更重要。
- 遵循等效且安全的代码库惯例。 如果代码库已有不同但可靠的机制,能够维持相同的安全性/属性边界(例如使用 Zod 而非
class-validator,或使用代码库标准的结构化日志记录器而非nestjs-pino),请遵循代码库的做法,不要在任务中途重写。 - 指出有害冲突并升级处理。 如果现有模式削弱了安全性、正确性、数据完整性或稳定契约,请明确指出,并在统一该模式或扩大其使用范围之前征求同意。
执行规范
在处理 NestJS 特定细节之前,请先遵循这些规则。它们旨在减少 LLM 常见的失误:默默做出假设、过度构建代码、大范围重构,以及未经验证就修复问题。
- 编码前先思考。 说明假设。如果任务有歧义,请先提问,或明确列出合理的解释,而不是默默选定一种。
- 核实易变信息。 版本、软件包 API、模型 ID、安装命令和 CLI 标志都可能变化。在提出建议前,请对照官方文档和仓库核实,切勿凭记忆作答。参见
35-source-of-truth-freshness.md。 - 编写新代码前先搜索。 先检查当前模块,再检查 shared/common/core 代码,看看是否已有可复用的工具函数、DTO、服务、守卫、拦截器、仓库或模式。
- 修复根本原因,而不是针对触发条件打补丁。 除非产品需求本身就是确定性的基于规则的路由,否则不要用一次性正则表达式、硬编码短语或手动 if/then 规则来修补广泛行为。
- 优先采用最小且正确的改动。 除非任务或代码库明确需要,否则不要添加臆测性的标志、抽象、辅助函数、配置或边缘情况处理。
- 谨慎编辑。 只修改满足请求所需的行。不要因为顺手就清理无关代码、注释或格式。
- 搜索并更新完整影响范围。 如果共享函数、契约、DTO、类型或行为发生变化,请扫描并更新调用方、测试、文档、示例及相关流程,直至整体保持一致。
- 修改共享行为前先询问。 如果复用现有代码需要改变共享语义、影响多个调用方或修改可复用契约,请停止并确认,不要默默扩大影响范围。
- 明确成功标准。 将模糊的请求转化为可验证的检查项:回归测试、单元测试、端到端测试、类型检查、Lint、构建,或具体的手动检查。
- 遇到困惑时停止。 尽早说明不确定之处。简短的澄清问题,比重写错误的代码成本更低。
打开 references/00-execution-discipline.md,查看完整清单和示例。
不可妥协事项(保障这些结果;优先采用这些模式)
每条规则都说明了原因,帮助你推理边缘情况,而不是盲目照搬。
- 控制器中不应包含业务逻辑。 控制器负责验证输入、委托给服务并组织响应,仅此而已。
原因: 控制器是精简的 HTTP 适配层。若在其中编写逻辑,就必须启动框架才能进行单元测试,还会把 HTTP 相关问题带入领域代码。参见
04-code-quality.md。 - 所有外部输入都必须经过 DTO。 请求体、查询参数和路径参数 DTO 使用
class-validator;全局启用ValidationPipe,并设置whitelist: true、forbidNonWhitelisted: true、transform: true。 原因: DTO 是拦截未知字段、错误类型和注入负载的唯一关卡。跳过这一步,就意味着信任客户端。参见09-validation.md。 - 绝不信任客户端。 URL 中的每个 ID 都要检查其是否属于已认证用户或组织。筛选和排序字段必须加入白名单。通过
whitelist防止批量赋值。 原因: IDOR 和批量赋值是最常见的两类应用层安全漏洞。只有在代码认为“只要令牌有效,负载就没问题”时,这些漏洞才会出现。参见11、12。 - 每个模块只负责自己的表。 禁止跨模块直接读取数据库。如果模块 B 需要模块 A 的数据,应调用 A 的服务(DI)或订阅 A 的事件。
原因: 共享表访问会让每次架构变更都变成需要跨团队协调的问题。模块会通过数据库而非 API 彼此耦合。参见
03-module-design.md。 - 数据库使用 snake_case,代码使用 camelCase。 表名用复数,列名用 snake_case,主键为
id,外键为<entity>_id。 原因: 每个生态系统都有自己的约定;混用会造成长期的映射错误,也会让临时 SQL 难以编写。应采用最难变更的一方(数据库)的约定。参见13-database-design.md。 - 响应遵循统一且稳定的契约。 单个资源的成功响应直接返回对象本身;列表成功响应返回
{ data, meta };错误响应返回{ code, message, details?, traceId },并使用正确的 HTTP 状态码。 原因: 统一的契约让客户端只需编写一个错误处理器和一个分页处理器,就能在各处使用;支持人员也能通过traceId排查问题。参见07、10。 - 所有列表端点都必须分页。 对可变或大型数据进行顺序浏览时,选择游标/键集分页;当按页码导航或获取精确总数是实际的产品需求时,选择偏移分页。两者都要在
meta中返回分页信息。 原因: 未分页的列表可能导致内存溢出或数据库宕机。合适的分页模型取决于用户体验、一致性要求和规模。参见08。 - 密钥只能来自环境变量,并在启动时用 Zod 验证。 代码中不得包含密钥,日志中也不得记录密钥。环境变量无效时,必须在开始处理流量前崩溃。
原因: 在启动时发现缺失或格式错误的环境变量只是轻微事故;若在运行时的关键路径上才发现,就会导致客户服务中断。参见
20、11。 - 使用结构化日志并进行脱敏。 生产环境使用
nestjs-pino输出 JSON;对authorization、cookie、set-cookie、password、token进行脱敏。每条日志都要包含关联 ID。 原因: 纯文本日志无法大规模查询;而一条未脱敏的Authorization标头就会造成长期凭据泄露。参见21。 - 测试边界,而非内部实现。 使用模拟依赖对服务进行单元测试;通过 HTTP 层对控制器进行端到端测试;绝不模拟被测类。
原因: 模拟被测代码只是在重复断言模拟对象。测试应验证契约(HTTP、数据库、外部调用),而不是实现。参见
23。 - 租户身份由服务器推导,并分层校验。 在多租户应用中,租户/组织 ID 来自已认证会话/JWT,绝不来自请求体。守卫、服务和仓储都要按租户筛选;测试要证明租户 A 无法查看租户 B 的数据。
原因: 跨租户数据泄露几乎总是因为漏掉了一处
WHERE tenant_id = ?,或过早信任了客户端提供的 ID。分层校验可确保某一层出现错误时也不会泄露数据。参见33。
高级工程师思维(决策树)
完整决策树位于 references/05-thinking-decision-trees.md。简要版:
这个文件应该放在哪里?
- 业务功能 →
modules/<feature>/ - 应用级基础设施(auth、redis、mail)→
core/<name>/ - 通用工具(管道、装饰器、类型)→
common/<kind>/ - 外部 API 客户端(Stripe、AWS)→
integrations/<provider>/ - CLI/CRON 任务 →
commands/<name>.command.ts - 领域事件发布器/监听器 →
events/<event-name>/
我应该创建新模块吗?
- 它有自己的数据库表吗?→ 是,创建新模块。
- 它有自己的生命周期/领域逻辑吗?→ 是,创建新模块。
- 它只是某个模块使用的辅助工具吗?→ 否,放进该模块的
utils/。 - 它被 2 个或更多模块使用,但不拥有数据吗?→
common/工具。
我现在应该重构吗?
- 我已经在编辑这段代码了吗?→ 是,边做边清理。
- 它会阻碍我当前的功能吗?→ 是,尽量少量重构。
- 它只是“难看”吗?→ 否,记下来,然后继续。
我应该跳过测试吗?
- 它是控制器吗?→ 否,始终编写 E2E 测试。
- 它是含有分支逻辑的服务吗?→ 否,始终编写单元测试。
- 它是没有逻辑的简单透传(例如
findById)吗?→ 可以跳过;控制器的 e2e 测试会覆盖它。 - 它是 DTO/类型吗?→ 无需测试;编译器就是测试。
规则索引(每条参考资料一行)
需要了解详情时,请阅读完整的参考文件。数字前缀用于保持顺序稳定。
| # | 文件 | 一句话规则 |
|---|---|---|
| 00 | 00-execution-discipline.md | 先思考,再根据文档核实易变事实(版本/API/模型 ID);保持改动精简,精准编辑,定义成功标准,并在声称完成前进行验证 |
| 01 | 01-folder-structure.md | src/{core,common,integrations,modules,events,commands} — 每类代码各有一个专属位置 |
| 02 | 02-naming-conventions.md | 变量使用 camelCase,类使用 PascalCase,数据库使用 snake_case,文件使用 kebab-case.ts,环境变量使用 SCREAMING_SNAKE |
| 03 | 03-module-design.md | 每个有界上下文对应一个模块;仅对真正的全应用基础设施使用 @Global() |
| 04 | 04-code-quality.md | 遵循 SOLID、使用构造函数 DI、编写纯工具函数和小函数;没有合理理由时不要使用 any |
| 05 | 05-thinking-decision-trees.md | 如何做出判断:代码放在哪里、何时重构、何时跳过测试 |
| 06 | 06-api-design.md | REST、使用复数名词、动词与语义相符、URI 版本控制 /v1/...、幂等键 |
| 07 | 07-standard-responses.md | 单条成功响应返回普通对象;列表返回 { data, meta };错误返回 { code, message, details?, traceId } |
| 08 | 08-pagination-filters-sorting.md | 顺序浏览使用游标/键集分页;确有页码/精确总数需求时使用偏移分页;使用 filter[field]=、sort=-createdAt;字段使用白名单 |
| 09 | 09-validation.md | 使用 class-validator DTO + 全局 ValidationPipe;使用 Zod 校验环境变量 + 解析运行时 JSON |
| 10 | 10-error-handling.md | 混合分类体系:HTTP 状态码 + 命名空间代码 + traceId;领域错误继承语义明确的 Nest 异常;使用一个全局过滤器,并添加 host.getType() + headersSent 检查,通过 PinoLogger 记录日志 |
| 11 | 11-security.md | 安全审查流程:OWASP Top 10、传输/CORS、注入/SSRF、密码哈希、速率限制、PII/审计,以及指向身份验证/webhook/上传规则的链接 |
| 12 | 12-authentication-patterns.md | 使用会话 Cookie(浏览器)或 Bearer JWT(移动端/服务端);静态存储时对会话/刷新令牌进行哈希;轮换刷新令牌;使用 revoked_before;Cookie 优先,且无效 Cookie 必须默认拒绝;身份验证错误使用 { code, message } |
| 13 | 13-database-design.md | 使用 snake_case、复数表名、外键 <entity>_id;在外键和查询路径上建立索引;使用 deleted_at、UUIDv7 或 bigint |
| 14 | 14-database-orm-patterns.md | raw pg / TypeORM / Prisma / Drizzle — 并列展示各种模式 |
| 15 | 15-migrations.md | 生产环境始终只允许向前迁移;未经两步发布,不得进行破坏性变更 |
| 16 | 16-cascade-rules.md | 自有数据使用 ON DELETE CASCADE;共享引用使用 RESTRICT;可选数据使用 SET NULL |
| 17 | 17-pipelines-interceptors-guards.md | 顺序:Guard → Interceptor(前置)→ Pipe → Handler → Interceptor(后置)→ Filter |
| 18 | 18-events.md | 进程内使用 EventEmitter2;跨服务或队列时使用 outbox 模式 |
| 19 | 19-background-jobs.md | 默认使用 BullMQ;处理器保持幂等;采用带退避的重试;为有毒消息设置 DLQ |
| 20 | 20-configuration.md | ConfigModule 设为全局;使用 Zod 模式;环境变量无效时在启动阶段快速失败 |
| 21 | 21-logging.md | 使用 nestjs-pino;生产环境使用 JSON;对机密信息进行脱敏;为每个请求生成关联 ID |
| 22 | 22-observability.md | 使用 OpenTelemetry 跟踪 + 指标;使用 Langfuse/Helicone 跟踪大模型调用 |
| 23 | 23-testing.md | 单元测试放在实现文件旁(*.spec.ts);端到端测试放在 test/;在边界处进行模拟;集成测试使用真实数据库 |
| 24 | 24-performance.md | 避免 N+1;合理设置连接池大小;有选择地缓存;以流式方式处理大型负载 |
| 24a | 24a-caching-patterns.md | 审慎使用缓存;使用稳定的命名空间键、TTL + 失效机制,并防止缓存击穿;缓存绝不能成为身份验证/配额/计费的唯一依据 |
| 25 | 25-documentation-swagger.md | 使用 @ApiTags / @ApiOperation / @ApiResponse;通过 @ApiProperty 自动生成 DTO 模式 |
| 26 | 26-ai-product-patterns.md | 使用具备提供方抽象、重试、回退和超时机制的大模型网关 |
| 27 | 27-ai-streaming-sse.md | 使用 SSE 端点;支持取消(中止上游请求);发送心跳;使用类型化事件词汇;重新连接后无法恢复 |
| 28 | 28-ai-usage-metering-cost.md | 为每次调用记录 token + 成本;按用户/组织/模型汇总;强制执行配额 |
| 29 | 29-code-review-checklist.md | 涵盖以上所有规则的 PR 审查清单 |
| 30 | 30-code-review-anti-patterns.md | 反模式目录,包含正反示例片段 |
| 31 | 31-rules-rationale-examples.md | 跨规则的规则、理由和正反示例,便于快速查阅 |
| 32 | 32-modern-nestjs-stack.md | 用于现代化/启动 NestJS 服务的决策清单;启动顺序、模块系统检查;不包含固定的版本矩阵 |
| 33 | 33-multi-tenancy-patterns.md | 由服务端推导租户身份,并在身份验证、guard、服务和仓库层强制执行;通过测试证明租户隔离 |
| 34 | 34-health-shutdown.md | 存活状态与就绪状态;使用一个关闭协调器;关闭前排空请求;单独排空工作进程 |
| 35 | 35-source-of-truth-freshness.md | 持久不变的规则留在本地;根据官方文档和仓库核实易变的 API/版本/模型 |
| 36 | 36-webhooks.md | 对原始字节验证签名(raw-body 配置 + timingSafeEqual);根据 (provider, event_id) 去重;入队后返回 2xx 确认(包括未处理的类型);对高风险事件重新获取权威状态;从已验证的负载中解析租户 |
| 37 | 37-file-uploads.md | 优先使用预签名的直接上传至存储桶(客户端使用 PUT,浏览器使用 POST 策略);在边界处限制大小/MIME;检测文件魔数;使用不透明且带租户前缀的存储键;由服务端计算哈希/大小/MIME;公开文件前进行 AV 扫描;对上传端点进行速率限制 |
| 38 | 38-decorators-scopes-dynamic-modules.md | 参数装饰器只从请求中提取数据;默认使用单例作用域;使用动态模块配置基础设施;forwardRef 是一种异味 |
| 39 | 39-exception-filters.md | 使用一个全局过滤器,将所有错误统一为 { code, message, details?, traceId };抛出类型明确的 HttpException 子类;绝不泄露内部信息 |
| 40 | 40-ddd-layered-architecture.md | 可选的三层 DDD 分层(经典分层 → 分层特性模块 → 六边形端口/适配器);依赖指向内层;领域层不依赖框架;对于 CRUD,默认的六类目录结构仍然更合适 |
| 41 | 41-n-plus-one-elimination.md | 通过统计查询次数证明 N+1(绝不靠猜测);除显而易见的循环外,还有六种模式;选择 JOIN、两次查询批量加载或每请求一个 DataLoader;绝不在联接集合上使用 SQL LIMIT;通过查询次数测试锁定修复 |
何时深入阅读
| 你正在进行…… | 阅读…… |
|---|---|
| 开始任何实现或 Bug 修复 | 00、05 |
| 开始新的功能模块 | 01、03、04 |
| 使用 DDD 分层 / 六边形架构 / 端口-适配器架构进行组织 | 40、01、03 |
| 启动或现代化改造一项服务 | 32、34、20 |
| 设计新的端点 | 06、07、08、09 |
| 统一返回错误 | 10 |
| 设计数据库表 | 13、14、15、16 |
| 为端点添加身份验证 | 11、12、17 |
| 添加多租户隔离 | 33、11、12、14 |
| 添加列表端点 | 07、08、41 |
| 添加缓存 | 24、24a |
| 修复响应缓慢的端点、N+1 问题或查询次数过多的问题 | 41、24、14、13 |
| 添加后台任务 | 19、34 |
| 编写测试 | 23 |
| 添加可观测性 | 21、22 |
| 添加健康检查 / 就绪检查 / 关闭处理 | 34、32 |
| 构建大模型功能 | 26、27、28、22 |
| 处理来自第三方的 Webhook | 36、11、19、21、33 |
| 接受文件上传 | 37、11、24 |
| 设计自定义装饰器 / 作用域提供程序 / 动态模块 | 38、03、17 |
| 设计全局异常筛选器 / 错误在线格式 | 39、07、10 |
| 提供版本 / 命令 / 模型方面的建议 | 35 |
| 审查 PR | 29、30,以及与差异相关的任何主题 |
代码审查模式
当用户要求你审查 PR、更改或差异时:
- 先从头到尾阅读差异,理解意图后再提出批评。
- 按照
29-code-review-checklist.md检查每一项,并标记通过 / 未通过 / 不适用。 - 对每个未通过项,引用相关参考文件中的具体章节(例如,“控制器中的业务逻辑——参见
04-code-quality.md→ 分层 / 反模式”)。 - 区分阻塞项(安全问题、数据丢失风险、违反不可妥协的规则)和建议(风格、轻微的命名问题、可选重构)。不要因建议而阻止通过。
- 如果发现反模式,请链接到
30-code-review-anti-patterns.md,并引用正确的写法。 - 审查结束时列出:(a) 阻塞项(编号列表),(b) 建议(项目符号列表),(c) 仅在解决阻塞项后才批准。
AI 产品附录
如果项目是 AI 产品后端(大模型、Agent、RAG、向量存储、流式传输、按用量计费),还要阅读:
26-ai-product-patterns.md— 与提供商无关的大模型网关、重试 / 回退27-ai-streaming-sse.md— SSE 端点模式、取消、心跳28-ai-usage-metering-cost.md— 令牌和成本计量、配额执行22-observability.md— 使用 Langfuse / Helicone 进行大模型追踪
其他所有内容(身份验证、数据库、错误处理、测试)对 AI 产品同样适用——AI 部分是一个模块,而非框架。
其他说明
- 框架细节之前先遵守执行纪律。 小而明确且经过验证的更改,胜过快速、宽泛而巧妙的更改。
- 规则 > 风格偏好。 这些是规则,因为它们能避免实际 Bug,或提升可维护性。本文不涉及风格争论。
- 一致性 > 巧妙。 如果仓库中已有一种功能等效但与此技能不同的成熟模式,只要该模式没有明显危害,就遵循仓库的模式。
- 不确定时,保持简单。 简单的代码易于审查、易于上手,也易于替换。这正是重点。