Stockio

Debug Node.js in VS Code: Launch, Attach and Inspect

Set up reliable Node.js debugging in VS Code, inspect failing requests with conditional breakpoints, and diagnose source-map and process-selection problems.

By Ravi Desai

DevOps & Security Engineer ·

On this page

Console logging works until a request passes through several asynchronous functions and the value you need disappears between messages. The VS Code debugger lets you stop at the decision point, inspect the current scope, and follow the actual execution path without adding permanent logging noise.

Prepare a small, repeatable failing request

Capture one input that reliably demonstrates the defect. If the failure involves JSON, inspect a sanitized payload with the JSON formatter before replaying it locally. Check missing fields, strings that should be numbers, and nested arrays. Do not paste production credentials or customer records into troubleshooting tools.

Write down the expected result and observed result. A useful example is “quantity zero should be rejected, but the endpoint returns a total.” This gives you a specific branch to inspect rather than an invitation to step through every line of the server.

Use a local development process with test data. Pausing a Node.js process can hold requests open and trigger client timeouts. Other workers or subprocesses may continue, so a breakpoint does not necessarily freeze the entire application. The VS Code Node.js debugging guide explains the available launch and attachment modes.

Launch the program directly before adding wrappers

For a small service, start with a launch configuration that points directly at the JavaScript entry file. This removes uncertainty about package-manager wrappers, development servers, and which child process actually runs the application.

{
"version": "0.2.0",
"configurations": [
  {
    "name": "Debug local API",
    "type": "node",
    "request": "launch",
    "program": "${workspaceFolder}/src/server.js",
    "cwd": "${workspaceFolder}",
    "console": "integratedTerminal",
    "skipFiles": ["<node_internals>/**"],
    "env": {
      "NODE_ENV": "development"
    }
  }
]
}

Save this as .vscode/launch.json, adapting the entry path to the repository. The working directory matters when code resolves relative file paths. The integrated terminal is useful for programs that expect terminal input; the Debug Console serves a different purpose and is not a general replacement for standard input.

Set a breakpoint in the request handler, start the configuration, and send your failing request. Confirm the breakpoint stops before adding complicated watches. If the application exits immediately, read the terminal error first: the debugger cannot compensate for missing dependencies or a wrong entry point.

Keep shared launch files free of credentials and machine-specific absolute paths. A configuration should explain how to start the program, not silently encode one developer's laptop layout.

Attach when another tool starts the process

Sometimes a framework command or existing terminal owns startup. Start Node with its inspector enabled, then attach VS Code to the inspector port. Use --inspect-brk when you need to stop before application startup code runs; plain --inspect normally lets execution continue immediately.

node --inspect-brk=127.0.0.1:9229 src/server.js
{
"name": "Attach local Node",
"type": "node",
"request": "attach",
"address": "127.0.0.1",
"port": 9229,
"skipFiles": ["<node_internals>/**"]
}

Add the second object to the configurations array. Keep the inspector on loopback only for normal local work. The Node.js debugging guide explains why exposing an inspector endpoint is dangerous: debugger access can enable arbitrary code execution in the process.

If a watcher restarts the server, verify whether the attach configuration reconnects to the new process. A stale attachment can look like ignored breakpoints. For containers or remote development, prefer the editor's supported remote workflow or a deliberate tunnel rather than binding the inspector publicly.

You can also try a JavaScript Debug Terminal for convenient automatic attachment. Regardless of the method, confirm the selected process is the one receiving your request, especially when a command starts multiple Node processes.

Inspect state without changing the bug accidentally

At a breakpoint, start with local variables and the call stack. Ask where the unexpected value first becomes wrong. Step over executes the current operation without following every called function; step into follows a call, and step out returns from the current function. Use these deliberately instead of repeatedly pressing the same key.

Add a conditional breakpoint such as request.body.quantity === 0 when the handler receives many requests. The expression must use names available at that location. A condition that throws or references nonexistent variables can confuse the investigation rather than narrow it.

Use logpoints for observations that do not require pausing. They are useful in loops or frequently called functions, although expensive expressions still add overhead. Avoid printing tokens, full authorization headers, or entire customer payloads just because the output is temporary.

The Debug Console evaluates expressions in the selected stack frame. Prefer inspection over mutation: calling a method may change state, send a request, or advance an iterator. A getter can also have side effects. If you deliberately change a variable to test a hypothesis, restart the scenario afterward before drawing conclusions.

Enable exception breakpoints when a failure is being swallowed. Start with uncaught exceptions, then consider caught exceptions if necessary. Pausing on every caught exception in a dependency-heavy application can be overwhelming, so narrow the reproduction first.

Resolve source maps and verify the fix normally

A hollow breakpoint often means the debugger cannot map your source file to executable code. For TypeScript compiled to JavaScript, enable source maps in the compiler and ensure the launch configuration's output-file patterns match the emitted JavaScript. Do not point at an old build directory while editing newer source files.

Read the VS Code TypeScript debugging documentation for the mapping model. Bundlers and runtime transpilers need their own compatible configuration; copying a plain compiler setup into every framework rarely works. Remove stale output and rebuild using the project's normal command when mappings no longer line up.

Check the loaded script path and active process before changing compiler settings. Duplicate source copies, symlinked packages, and monorepo working directories can all send you to the wrong file. Source-map accuracy is a prerequisite for trusting line-level observations.

Once you understand the failure, write a focused regression test and run it outside the debugger. Timing-sensitive defects may disappear while paused. Confirm ordinary startup, then remove temporary watches or diagnostic settings that could expose sensitive data. Keep useful team configurations documented alongside broader development practices and the dev-tools collection.

Key takeaways

  • Reproduce one concrete failure before stepping through the application.
  • Launch directly when possible; attach deliberately when another process owns startup.
  • Keep inspector access local and verify the actual process receiving requests.
  • Use conditions and logpoints to reduce noise without mutating state.
  • Trust source mappings only after checking emitted files and loaded paths.
Featured sponsor

Reach designers & developers at work

Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.

Become a sponsor

Keep reading

Related articles

More in Dev Tools & AI Coding
Dev Tools & AI Coding

8 Best AI Code Review Tools

Compare eight AI-assisted code review tools for pull requests, security checks, and repository workflows, with practical cautions about false alarms, context, and human responsibility.

Includes: JSON formatterSofia MarchettiSofia Marchetti
Dev Tools & AI Coding

15 Best VS Code Extensions for Web Development

Find useful VS Code extensions for formatting, debugging, writing and remote work. This practical list explains what each add-on does, who benefits, and when built-in features are enough.

Includes: JSON formatterSofia MarchettiSofia Marchetti
Dev Tools & AI Coding

Turborepo Monorepo With pnpm Workspaces

Set up pnpm workspace boundaries and Turborepo task caching with explicit package dependencies, reliable outputs, and a structure that stays understandable as teams grow.

Includes: JSON formatterSofia MarchettiSofia Marchetti
Dev Tools & AI Coding

Chrome DevTools Performance Panel: From Trace to Fix

Record focused Chrome Performance traces, identify expensive main-thread work, and verify targeted fixes with repeatable scenarios instead of guessing from a flame chart.

Includes: JSON formatterRavi DesaiRavi Desai
Dev Tools & AI Coding

direnv and .env Files: Managing Local Environment Variables

Make local project configuration predictable with .env templates, explicit loading rules, direnv approval, and checks that catch missing values before startup.

Includes: JSON formatterRavi DesaiRavi Desai
Dev Tools & AI Coding

AI Tailwind CSS: Get Clean Styles From AI Assistants

AI assistants can produce a convincing interface quickly, but the stylesheet often reveals how little was specified. Arbitrary spacing, duplicated utilities and brittle positioning accumulate when every prompt.

Includes: JSON formatterRavi DesaiRavi Desai