Even with good tests, real bugs still happen. This lesson covers the tools Node.js provides for debugging, from simple console logging to the built-in inspector with real breakpoints.
Debugging Beyond console.log
console.log() is often the fastest way to check a value, but for anything beyond a quick check, Node.js's built-in inspector gives you real breakpoints, step-through execution, and variable inspection, the same debugging experience as browser DevTools, connected directly to a running Node.js process.
Running node --inspect starts your script with a debugging port open; you then connect to it from Chrome DevTools (via chrome://inspect) or directly from an editor like VS Code or Cursor, which can attach a debugger with a single click.
node --inspect index.js
# or, to pause immediately on the first line:
node --inspect-brk index.js
--inspect-brk is especially useful for debugging issues that happen very early in startup, since it pauses before any of your code runs.
Debugging Tools at a Glance
console.log(value) // quick inline checks
console.table(arrayOfObjects) // readable tabular output
debugger; // breakpoint in code, with --inspect
node --inspect script.js // attach Chrome DevTools or an editor
console.table() is often clearer than console.log() for arrays of objects.
The debugger; statement pauses execution at that line when a debugger is attached.
VS Code and Cursor can attach to a Node.js process directly with a built-in "Attach to Node Process" configuration.
console.error() should be used for actual errors, keeping them distinguishable from regular logs.
Debugging Cheatsheet
Common debugging commands and techniques.
Technique
Command / Usage
Basic logging
console.log(value)
Tabular logging
console.table(data)
Start with inspector
node --inspect index.js
Pause on first line
node --inspect-brk index.js
Code breakpoint
debugger; statement in source
Stack trace on error
console.trace() or error.stack
Debugging Asynchronous Code
Stepping through async code can be confusing because execution jumps between the event loop and your code. Modern debuggers (Chrome DevTools, VS Code) support "async stack traces", showing the chain of await calls that led to the current point, even across multiple asynchronous hops.
Diagnosing Memory and CPU Issues
Beyond logic bugs, Node.js apps can suffer from memory leaks or CPU bottlenecks. node --inspect also exposes a Memory tab (for heap snapshots) and a CPU profiler in Chrome DevTools, letting you see exactly which objects are accumulating or which functions are consuming the most CPU time.
Heap snapshots help identify objects that are never garbage collected (memory leaks).
CPU profiles show which functions consume the most time, useful for finding blocking hotspots.
process.memoryUsage() gives a quick programmatic snapshot without a full profiler session.
Common Mistakes
Relying solely on scattered console.log() calls for complex bugs instead of using real breakpoints.
Forgetting to remove debug-only console.log() statements before committing code.
Not distinguishing console.error() from console.log(), making logs harder to filter by severity.
Skipping heap/CPU profiling entirely when facing a performance issue that isn't an obvious logic bug.
Key Takeaways
Node.js's built-in inspector (node --inspect) provides real breakpoints and step-through debugging.
console.table() and console.trace() offer more structured debugging output than plain console.log().
Modern debuggers support async stack traces, making it easier to trace execution across await calls.
Heap snapshots and CPU profiles help diagnose memory leaks and performance bottlenecks respectively.
Pro Tip
Learn your editor's "Attach to Node Process" or built-in Node.js debug configuration once, it takes a few minutes to set up and pays for itself the very next time you need to trace a confusing bug step by step instead of guessing with scattered log statements.
You now have real debugging tools beyond console.log. Next, learn structured logging, essential for diagnosing issues in production where you can't attach a debugger.