{T}

Node.js与NestJS调试实战

Node.js与NestJS调试实战

核心知识点

一、调试基础认知

1.1 为什么要调试

概念说明
调试的本质是“可观察执行过程”:在断点位置查看变量、调用栈和执行路径,定位逻辑错误。

语法/用法

  • 关键动作:打断点、单步执行、查看变量、查看调用栈
  • 常用命令:Continue、Step Over、Step Into、Step Out

代码示例

ts
function sum(a: number, b: number) {
  const result = a + b;
  return result;
}

console.log(sum(1, 2));

注意事项

  • 调试不是“跑起来就行”,而是“验证每一步是否符合预期”。
  • 建议先复现问题,再进入调试,避免无目标地打断点。

二、方式一:VS Code 调试

2.1 单文件 Node.js 调试

概念说明
适用于只有一个入口文件(如 index.js / index.ts)的简单程序。

语法/用法

  1. 打开 VS Code 左侧“运行和调试”(小虫子图标)
  2. 生成 launch.json
  3. 选择 Node.js 配置
  4. 指定入口 program

代码示例

json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug index.js",
      "program": "${workspaceFolder}/index.js",
      "skipFiles": ["<node_internals>/**"]
    }
  ]
}

注意事项

  • 若出现 preLaunchTask 报错,可先移除该字段再验证。
  • 左上角显示的调试项名称来自 launch.jsonname

2.2 NestJS 项目通过 npm script 调试

概念说明
NestJS 通常不是直接运行单文件,而是通过脚本启动(如 start:debug)。

语法/用法

  • package.json 配置:
    • start:dev:开发热更新
    • start:debug:带 inspector 的调试模式
  • VS Code 里选择 “Node.js: 通过 npm 启动” 的配置

代码示例

json
{
  "scripts": {
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch"
  }
}
json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Nest Debug (npm)",
      "runtimeExecutable": "pnpm",
      "runtimeArgs": ["run", "start:debug"],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    }
  ]
}

注意事项

  • 如果使用 NVM,可额外配置 runtimeVersion(例如 18.15.0)。
  • 断点停不住时,先确认是否请求到了对应路由。

2.3 调试动作说明

概念说明
调试效率取决于你是否正确使用单步控制。

语法/用法

  • Continue:继续运行到下一个断点
  • Step Over:执行当前行,不进入函数内部
  • Step Into:进入函数内部
  • Step Out:跳出当前函数

代码示例

ts
@Get()
getHello() {
  const message = this.appService.getHello(); // Step Into 可进入 getHello
  return message;
}

注意事项

  • 只看最终输出不够,要结合调用栈判断调用链是否正确。

三、方式二:WebStorm 调试

WebStorm 与 VS Code 调试思想一致,差异主要在配置界面与按钮位置。

语法/用法

  1. 打开 Edit Configurations
  2. 新建 npm 配置
  3. 选择 script:start:debugstart:dev
  4. 设置 Node 版本、Package Manager(npm/pnpm)
  5. 打断点后点击 Debug(小虫子)

代码示例

text
Run/Debug Configurations
- Type: npm
- Script: start:debug
- Node interpreter: v18.15.0
- Package manager: pnpm

注意事项

  • 请求未发出前,路由内部断点不会触发。
  • 在函数内部调试后可用 Step Out 快速回到上层调用。

四、方式三:命令行 + 浏览器 DevTools

当不方便使用 IDE(远程机器、临时环境)时,可用 Node Inspector + Chrome DevTools 调试

语法/用法

  1. 命令行启动调试进程
  2. 浏览器打开 DevTools 的 Node 调试入口
  3. 在代码中使用 debugger 或在 DevTools 打断点
  4. 访问接口触发断点

代码示例

bash
pnpm run start:debug
# 启动后会看到:Debugger listening on ws://127.0.0.1:9229/...
ts
@Get()
getHello() {
  debugger;
  console.log('hello world');
  return 'hello world';
}

注意事项

  • 默认调试端口常见为 9229,端口占用时需调整。
  • 删除 debugger 后,仍可在 DevTools 源码面板继续打断点。

五、课堂内容纠错与规范化

课堂表述规范写法说明
launch 点Jasonlaunch.jsonVS Code 调试配置文件
start DVstart:devNestJS 开发脚本标准命名
start debugstart:debug建议统一冒号分隔脚本名
inspect 参数--inspect / --debugNode Inspector 调试参数
next s / NASA GSNestJS框架名称统一
console hello wordconsole.log('hello world')方法与字符串拼写规范

六、调试最佳实践

6.1 高效断点策略

概念说明
“少而准”的断点优于“到处打断点”。

语法/用法

  • 优先在入口层打首断点:Controller
  • 第二断点放业务分支:Service
  • 第三断点放异常分支:throw 前后

代码示例

ts
@Get(':id')
findOne(@Param('id') id: string) {
  // 断点1:检查参数是否正确
  return this.userService.findOne(id);
}

注意事项

  • 每次只保留当前问题相关断点,避免干扰。
  • 出现“断点灰色未命中”时,优先检查 sourcemap 与编译产物是否同步。

6.2 日志与断点结合

概念说明
日志适合看全局趋势,断点适合看局部状态,二者结合效率最高。

语法/用法

  • 关键路径保留结构化日志
  • 难定位分支通过断点观察变量实时值

代码示例

ts
console.log('[UserController] incoming request', { path: '/user', method: 'GET' });

注意事项

  • 调试日志建议带模块前缀,便于检索。
  • 问题修复后,临时日志要及时清理。

代码实战案例

需求描述

实现一个可调试的 GET /api/v1/user 接口,并通过 VS Code 触发断点、查看变量。

完整实现代码

ts
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.setGlobalPrefix('api/v1');
  await app.listen(3000);
}
bootstrap();

// src/app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('user')
  getUser() {
    const data = this.appService.getUserList();
    return {
      code: 0,
      data,
      message: '请求用户列表成功',
    };
  }
}

// src/app.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getUserList() {
    const users = [
      { id: 1, name: 'Tom' },
      { id: 2, name: 'Jerry' },
    ];
    return users;
  }
}

// package.json
{
  "scripts": {
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch"
  }
}

// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Nest Debug (pnpm)",
      "runtimeExecutable": "pnpm",
      "runtimeArgs": ["run", "start:debug"],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    }
  ]
}

代码逐行解析

代码位置关键行作用说明
main.tsapp.setGlobalPrefix('api/v1')统一接口前缀,调试请求路径更明确
app.controller.tsconstructor(private readonly appService...)通过 DI 注入服务层能力
app.controller.tsconst data = this.appService.getUserList()调试时可观察 data 实时值
app.service.tsgetUserList()业务数据集中在 Service,便于单步排查
package.jsonstart:debug启动 inspector,供 IDE/浏览器接入
.vscode/launch.jsonruntimeArgs由 VS Code 触发脚本并进入可调试状态

常见问题与解决方案

问题根因解决方案
断点不生效请求没命中路由先访问正确 URL,再观察断点
启动时报脚本不存在package.json 没有 start:debug添加 start:debug: nest start --debug --watch
端口冲突(9229)调试端口被占用结束占用进程或改 inspector 端口
VS Code 报 preLaunchTask 错误默认任务不存在删除 preLaunchTask 或补齐 tasks 配置
浏览器能开页面但 IDE 不停断点启动方式是普通 start:devstart:debug 启动调试进程
WebStorm 调试慢Node 解释器/包管理器配置不匹配在配置中校准 Node 与 pnpm 路径

学习要点总结

  1. 三种调试方式本质一致:都依赖 Node Inspector 和断点控制。
  2. NestJS 推荐脚本化调试:start:debug + IDE 配置联动。
  3. 请求触发型接口断点必须“发请求才会停”,这是最常见误区。
  4. Step Over / Into / Out 要熟练,能显著提升排错效率。
  5. 极端环境下可用“命令行 + DevTools”完成无 IDE 调试。

延伸学习资源

  • 官方文档:
  • 练习建议:
    • 练习 1:给 GET /api/v1/userPOST /api/v1/user 分别打断点,比较调用链
    • 练习 2:在 Service 中制造一个异常,观察调用栈定位过程
    • 练习 3:仅用命令行 + 浏览器完成一次完整断点调试
    • 练习 4:分别用 VS Code 和 WebStorm 复现同一 bug,比较效率差异