Debugging Node and TypeScript in VS Code
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": truefor the config used byts-node/tsx/ build output you debug.- If using path aliases (
@/), ensure the runtime resolver (tsconfig-paths/registeror bundler) matches VS Code’ssourceMapPathOverrideswhen 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.”