Overview
Node.js provides powerful debugging capabilities through the V8 Inspector protocol, enabling you to debug applications using Chrome DevTools, VS Code, and other debugging clients.
Command-Line Debugger
Node.js includes a built-in command-line debugging utility for simple stepping and inspection.
Starting the Debugger
Start Node.js with the inspect argument:
Output:
< Debugger listening on ws://127.0.0.1:9229/621111f9-ffcb-4e82-b718-48a145fa5db8
< For help, see: https://nodejs.org/en/docs/inspector
connecting to 127.0.0.1:9229 ... ok
< Debugger attached.
Break on start in myscript.js:2
1 // myscript.js
> 2 global.x = 5;
3 setTimeout(() => {
4 debugger;
debug>
The debugger automatically breaks on the first executable line.
Resume on Start
To run until the first debugger statement instead of breaking immediately:
NODE_INSPECT_RESUME_ON_START = 1 node inspect myscript.js
Debugger Commands
Stepping Commands
Breakpoint Commands
# Set breakpoint on current line
setBreakpoint ()
sb ()
# Set breakpoint on specific line
setBreakpoint(line )
sb(line )
# Set breakpoint in function
setBreakpoint( 'fn()' )
# Set breakpoint in file
setBreakpoint( 'script.js' , 1 )
# Conditional breakpoint
setBreakpoint( 'script.js' , 1, 'num < 4' )
# Clear breakpoint
clearBreakpoint( 'script.js' , 1 )
cb( 'script.js' , 1 )
Print backtrace of current execution frame
List source code with 5 line context
Add expression to watch list
List all watchers and their values
Open debugger REPL for evaluation
Execute expression and print value
Using Watchers
Watch expression values at each breakpoint:
// Add a watcher
watch ( 'myVariable' )
watch ( 'obj.property' )
// List all watchers
watchers
// Remove a watcher
unwatch ( 'myVariable' )
REPL Mode
Evaluate code in the debugging context:
debug > repl
Press Ctrl+C to leave debug repl
> x
5
> 2 + 2
4
> myFunction ()
'result'
Press Ctrl+C to exit REPL mode.
V8 Inspector Protocol
The V8 Inspector enables powerful debugging with Chrome DevTools and other clients.
Inspector Flags
--inspect
--inspect-wait
--inspect-brk
Start with inspector enabled. Code runs immediately. Output: Debugger listening on ws://127.0.0.1:9229/dc9010dd-f8b8-4ac5-a510-c1a114ec7d29
For help, see: https://nodejs.org/en/docs/inspector
Wait for debugger to attach before executing code. node --inspect-wait index.js
Break on the first line as soon as debugger attaches. node --inspect-brk index.js
Custom Port
Specify a custom debugging port:
node --inspect=9222 index.js
node --inspect=0.0.0.0:9222 index.js # Listen on all interfaces
Binding the inspector to a public IP:port combination is insecure. Ensure proper firewall rules are in place.
Start Node.js with inspector
node --inspect-brk app.js
Open Chrome
Navigate to chrome://inspect in Google Chrome
Configure target discovery
Click “Configure” and add localhost:9229 to the target list
Inspect
Click “inspect” under your Node.js process
Sources Panel : Set breakpoints, step through code
Console : Evaluate expressions in the current context
Profiler : Record CPU profiles
Memory : Take heap snapshots
Network : Inspect network requests (with --experimental-network-inspection)
VS Code Debugging
Launch Configuration
Create .vscode/launch.json:
{
"version" : "0.2.0" ,
"configurations" : [
{
"type" : "node" ,
"request" : "launch" ,
"name" : "Launch Program" ,
"skipFiles" : [ "<node_internals>/**" ],
"program" : "${workspaceFolder}/app.js"
},
{
"type" : "node" ,
"request" : "attach" ,
"name" : "Attach to Process" ,
"port" : 9229 ,
"restart" : true
}
]
}
Debug Current File
{
"type" : "node" ,
"request" : "launch" ,
"name" : "Debug Current File" ,
"program" : "${file}" ,
"skipFiles" : [ "<node_internals>/**" ]
}
Debug with Arguments
{
"type" : "node" ,
"request" : "launch" ,
"name" : "Launch with Args" ,
"program" : "${workspaceFolder}/app.js" ,
"args" : [ "--config" , "production.json" ],
"env" : {
"NODE_ENV" : "development"
}
}
Using the Inspector API
Programmatically control the inspector from your code.
Opening the Inspector
const inspector = require ( 'node:inspector' );
// Open inspector on default port
inspector . open ();
// Open on specific port
inspector . open ( 9229 , 'localhost' );
// Wait for debugger to attach
inspector . open ( 9229 , 'localhost' , true );
CPU Profiling
import { Session } from 'node:inspector/promises' ;
import fs from 'node:fs' ;
const session = new Session ();
session . connect ();
await session . post ( 'Profiler.enable' );
await session . post ( 'Profiler.start' );
// Run code to profile
performExpensiveOperation ();
const { profile } = await session . post ( 'Profiler.stop' );
fs . writeFileSync ( './profile.cpuprofile' , JSON . stringify ( profile ));
Heap Snapshots
import { Session } from 'node:inspector/promises' ;
import fs from 'node:fs' ;
const session = new Session ();
const fd = fs . openSync ( 'profile.heapsnapshot' , 'w' );
session . connect ();
session . on ( 'HeapProfiler.addHeapSnapshotChunk' , ( m ) => {
fs . writeSync ( fd , m . params . chunk );
});
await session . post ( 'HeapProfiler.takeHeapSnapshot' , null );
session . disconnect ();
fs . closeSync ( fd );
Taking Heap Snapshots from Debugger
From the command-line debugger:
debug > takeHeapSnapshot ( 'snapshot.heapsnapshot' )
Debugging Best Practices
Add debugger; statements in your code to create breakpoints: function processData ( data ) {
debugger ; // Execution will pause here
return data . map ( item => item * 2 );
}
For TypeScript or transpiled code, enable source maps: node --enable-source-maps dist/app.js
Focus on your code by skipping Node.js internals in VS Code: {
"skipFiles" : [ "<node_internals>/**" , "node_modules/**" ]
}
Use Conditional Breakpoints
Break only when specific conditions are met: setBreakpoint( 'app.js' , 42, 'user.id === 123' )
Remote Debugging
SSH Tunneling
Debug a remote Node.js application securely:
# On remote server
node --inspect=127.0.0.1:9229 app.js
# On local machine
ssh -L 9229:localhost:9229 user@remote-server
Then connect Chrome DevTools to localhost:9229.
Troubleshooting
Verify the port is not blocked by firewall
Check if another process is using port 9229
Ensure you’re connecting to the correct IP/port
Enable source maps: --enable-source-maps
Ensure source map files (.map) are present
Check sourceRoot in tsconfig.json
Verify code is actually being executed
Check if using —inspect-brk for early breakpoints
Ensure file path matches exactly
Next Steps
Performance Profile and optimize your code
Testing Write and run tests