VS Code `launch.json` for Node Debugging Is Worth Learning Before You Blame the Runtime
A practical VS Code Node debugging guide that explains launch.json, when to use launch versus attach, and why a small debug config can save more time than another round of console.log guessing.
Why this matters more than people admit: many developers rely on console logging far past the point where a real debugger would be faster. Then when a bug becomes stateful or timing-sensitive, they realize they never really learned the debug configuration they have been skipping.
What launch.json is doing
In VS Code, launch.json defines how the debugger should run or attach to your application. For Node projects, that usually means one of two models:
- start the process under the debugger
- attach to an already-running process
Both are useful, and choosing the wrong one is a common source of confusion.
A basic Node launch config
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Run current file",
"program": "${file}"
}
]
}This is enough to understand the model. VS Code launches Node itself, attaches the debugger immediately, and lets breakpoints work from process start.
Why launch is useful
Use launch when:
- you want a repeatable debug entrypoint
- the bug happens very early
- the app is simple enough for the editor to start directly
That is why it is a strong default for small scripts, APIs, and many single-service Node apps.
When attach is better
Sometimes the process is already running in another way. Maybe a framework script, a custom startup flow, or a containerized environment is controlling how the app begins.
In that case, attach is often cleaner because you are telling VS Code to connect to an existing debug-enabled process instead of trying to recreate the startup logic inside the editor.
The practical lesson is that debugger confusion often comes from mixing up “how the app starts” with “how I want to inspect it.”
Why people think breakpoints are broken
Usually one of these is true:
- the wrong request type is being used
- the program path is wrong
- source maps or build output are not aligned
- the process was not launched in a debuggable way
That fourth point matters especially in TypeScript or framework-heavy projects. The editor cannot attach to debug state that the runtime never exposed.
A healthier debugging habit
Before changing runtime flags or blaming Node, ask:
- do I actually need
launchorattach - is the editor starting the right file
- is my built code the code I think I am debugging
- am I avoiding the debugger because I never configured it once properly
That last question is more common than people like to admit.
A practical TypeScript caution
Many Node projects are not running source files directly. They are running built output, a framework wrapper, or a transpiled path. That is why debugging feels “off” even when the config looks reasonable. The breakpoint problem may not be the debugger at all. It may be that the runtime path and the source path stopped matching in your head.
This is exactly why it helps to keep the first debug configuration extremely honest and minimal before adding more abstraction.
Final recommendation
If you work in Node regularly, learning one good launch.json setup is worth the time. It turns the debugger from a vague optional feature into part of normal development. Most developers do not need a giant debug taxonomy. They need one clean config they trust and the judgment to know when launching and attaching are different problems.