VS Code 550 ships js-debug, a CDP client with an editor around it. F5 on a JavaScript file runs node --inspect-brk for you; for anything real, commit a .vscode/launch.json.
{
"version": "0.2.0",
"configurations": [
{ "type": "node", "request": "launch", "name": "Run server",
"program": "${workspaceFolder}/src/server.mjs", "console": "integratedTerminal",
"skipFiles": ["<node_internals>/**", "${workspaceFolder}/node_modules/**"] },
{ "type": "node", "request": "attach", "name": "Attach", "port": 9229, "restart": true }
]
}skipFiles keeps the stepper out of Node's internals — the difference between stepping into your handler and stepping into node:internal/streams. Use the attach configuration for a container or a process you started yourself: forward port 9229, and restart: true reconnects when it restarts.
Auto Attach removes even that step: debug.javascript.autoAttachFilter takes smart (scripts outside node_modules, plus known runners), always or onlyWithFlag, and the Toggle Auto Attach command switches between them. Two features beat breakpoints in a server: logpoints print an expression without pausing or editing the file, and conditional breakpoints stop only when order.id === 4711. The Caught Exceptions checkbox pauses the moment a rejection is created, before anyone swallows it.
Node's own CLI debugger speaks the same protocol when no editor is at hand.
$ node inspect cart.mjs
connecting to 127.0.0.1:9229 ... ok
debug> sb(16)
>16 return Math.round(cents * (1 - rate));
debug> cont
break in cart.mjs:16
debug> exec code
'save10'
debug> exec Object.keys(table)
[ 'SAVE10', 'SAVE25' ]