Debugging Node.js
The Node.js Inspector
Section titled “The Node.js Inspector”Node ships a built-in inspector based on the Chrome DevTools Protocol. Start it with the --inspect flag and connect any CDP-compatible debugger — Chrome DevTools, VS Code, or WebStorm.
# Start with inspector (process continues running, connects on demand)node --inspect src/app.js
# Start AND pause on the very first line of your codenode --inspect-brk src/app.jsAfter launching, open chrome://inspect in Chrome, click “Open dedicated DevTools for Node”, and you get full breakpoints, call stacks, scope inspection, heap snapshots, and CPU profiling — exactly the same panel you use for browser debugging.
Connecting with VS Code
Section titled “Connecting with VS Code”VS Code detects Node inspector automatically. Add a .vscode/launch.json:
{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Debug App", "program": "${workspaceFolder}/src/app.js", "runtimeArgs": ["--inspect-brk"] }, { "type": "node", "request": "attach", "name": "Attach to running node", "port": 9229 } ]}Press F5 to launch or Ctrl+Shift+P → “Attach to Node Process” to connect to an already-running --inspect server.
The debugger statement
Section titled “The debugger statement”Insert debugger; anywhere in your JavaScript. When the inspector is attached, execution pauses at that exact line — like setting a breakpoint in code:
function calculateTotal(items) { let total = 0; for (const item of items) { debugger; // execution pauses here when inspector is open total += item.price * item.qty; } return total;}When no inspector is connected, debugger is silently ignored. Remove or gate debugger statements before deploying to production.
console methods beyond console.log
Section titled “console methods beyond console.log”The console object has far more methods than most developers use:
// Tabular output — great for arrays of objectsconst users = [ { name: 'Alice', age: 30, role: 'admin' }, { name: 'Bob', age: 25, role: 'user' },];console.table(users);// Prints a formatted ASCII table with columns: name, age, role
// Deep inspection — prints non-enumerable properties and prototype chainconst map = new Map([['key', 'value']]);console.dir(map, { depth: 3, colors: true });
// Timing a code blockconsole.time('database-query');// ... run expensive operation ...console.timeEnd('database-query');// Prints: database-query: 42.183ms
// Count invocationsfunction hit() { console.count('hit');}hit(); // hit: 1hit(); // hit: 2hit(); // hit: 3
// Group related outputconsole.group('User details');console.log('name: Alice');console.log('role: admin');console.groupEnd();
// Conditional breakpoint-style assertionconsole.assert(1 === 2, 'Math is broken'); // prints assertion errorconsole.assert(1 === 1, 'This never prints');Practical debugging workflow
Section titled “Practical debugging workflow”# 1. Add --inspect-brk to pause before any code runsnode --inspect-brk src/server.js
# 2. Open chrome://inspect → click your target
# 3. Set breakpoints in the Sources panel, or hit the existing debugger statement
# 4. Step through: F10 (step over), F11 (step into), F8 (resume)
# 5. Inspect variables in the Scope panel or hover over them
# 6. Use the Console panel to run expressions in the current scopeDebugging with nodemon (watch + inspect)
Section titled “Debugging with nodemon (watch + inspect)”# Auto-restart AND keep inspector open on changesnpx nodemon --inspect src/app.js
# With ts-node for TypeScriptnpx nodemon --exec "node --inspect --loader ts-node/esm" src/app.ts