Skip to main content
Juliano Alves
Back to blog

Debugging Node and TypeScript in VS Code

4 min read
By Juliano Alves

Good debugging setup removes hours of console.log archaeology—especially with async code where stack traces jump through framework internals.

VS Code’s debugger speaks the Chrome DevTools Protocol. Node exposes it with --inspect / --inspect-brk. TypeScript adds the wrinkle of source maps and path aliases.

Single-file Vitest run#

.vscode/launch.json:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Vitest: current file",
      "runtimeExecutable": "pnpm",
      "runtimeArgs": ["exec", "vitest", "run", "${file}"],
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    }
  ]
}

Swap pnpm for yarn or npx to match the repo. Running one file keeps feedback fast.

Jest equivalent#

{
  "type": "node",
  "request": "launch",
  "name": "Jest: current file",
  "program": "${workspaceFolder}/node_modules/jest/bin/jest.js",
  "args": ["--runInBand", "${fileBasenameNoExtension}"],
  "cwd": "${workspaceFolder}",
  "console": "integratedTerminal"
}

--runInBand avoids worker isolation hiding breakpoints in some setups.

Attach to a running server#

Start the process with inspection:

node --inspect-brk=9229 dist/server.js

Then attach:

{
  "type": "node",
  "request": "attach",
  "name": "Attach 9229",
  "port": 9229,
  "restart": true,
  "localRoot": "${workspaceFolder}",
  "remoteRoot": "${workspaceFolder}"
}

restart: true reconnects when nodemon restarts the child process—essential for local API work.

Compound launch configs#

Debug client + server together:

{
  "compounds": [
    {
      "name": "Full stack",
      "configurations": ["Next.js: debug server-side", "Chrome: attach"],
      "stopAll": true
    }
  ]
}

(Exact names must match other entries in launch.json.)

Environment variables#

Prefer envFile in launch config pointing to .env.local for secrets that should not be committed. For CI, inject via pipeline secrets—not checked-in files.

Source maps checklist#

  • tsconfig.json: "sourceMap": true for the config used by ts-node / tsx / build output you debug.
  • If using path aliases (@/), ensure the runtime resolver (tsconfig-paths/register or bundler) matches VS Code’s sourceMapPathOverrides when debugging compiled output.

Monorepos#

Set "cwd" per package and use workspace-specific tsconfig references. Wrong cwd is the #1 reason breakpoints appear gray/unbound.

Conditional breakpoints and logpoints#

Right-click a breakpoint → Edit → expression (e.g. user.id === 'broken-one'). Logpoints print without pausing—useful in hot paths.

Summary#

Invest in launch.json once: per-tool test commands, attach configs for long-running servers, compounds for full stack. Verify source maps and cwd when breakpoints misbehave—usually configuration, not the debugger “being broken.”

© 2026 Juliano Alves. All rights reserved.