Debugging Node.js apps with Visual Studio Code debugger
Tags: node, vscode, debugging
Published: 2022-12-28 13:58:00

---
title: Debugging Node.js apps with Visual Studio Code debugger
date: 2022-12-28 13:55:00 UTC
published: true
tags: ['node', 'vscode', 'debugging']
canonical_url: https://sevic.dev/notes/debugging-nodejs-vscode/
---

Rather than doing it with console logs, debugging with a debugger and breakpoints is recommended. VSCode provides a built-in debugger for JavaScript-based apps.

This post covers configuring and running a debugger for various Node.js apps in VSCode.

### Configuration basics

VSCode configurations can use runtime executables like npm and ts-node. The executables mentioned above should be installed globally before running the configurations.

A configuration can use the `program` field to point to the binary executable package inside `node_modules` directory to avoid installing packages globally.

Runtime executables and programs can have arguments defined in `runtimeArgs` and `args` fields, respectively.

A configuration can have different requests:

- `attach` - the debugger is attached to the running process
- `launch` - the debugger launches a new process and wraps it

There are multiple configuration types:

- `node` - runs the program from `program` field, and logs are shown in debug console
- `node-terminal` - runs the command from `command` field and shows the logs in the terminal

The configuration file is `.vscode/launch.json`. The selected configuration on the Run and Debug tab is used as the default one.

### Launch configs

#### Node

Below are examples of configurations for running Node processes with the debugger.

```json
{
  "version": "0.2.0",
  "configurations": [
    // ...
    {
      "name": "Launch script in debug console",
      "program": "index.js", // update entry point
      "request": "launch",
      "type": "node",
      "skipFiles": [
        "<node_internals>/**"
      ]
    },
    {
      "name": "Launch script in the terminal",
      "command": "node index.js", // update entry point
      "request": "launch",
      "type": "node-terminal",
      "skipFiles": [
        "<node_internals>/**"
      ]
    }
  ]
}
```

#### Running npm scripts in debug mode

The debugger launches the following script in both configurations, `dev` in this case.

```json
{
  "version": "0.2.0",
  "configurations": [
    // ...
    {
      "name": "Launch dev script in debug console",
      "runtimeExecutable": "npm",
      "runtimeArgs": [
        "run",
        "dev"
      ],
      "request": "launch",
      "type": "node",
      "skipFiles": [
        "<node_internals>/**"
      ]
    },
    {
      "name": "Launch dev script in the terminal",
      "command": "npm run dev",
      "request": "launch",
      "type": "node-terminal"
    }
  ]
}
```

#### ts-node

The following configurations will wrap the debugger around `ts-node` entry point.

```json
{
  "version": "0.2.0",
  "configurations": [
    // ...
    {
      "name": "Launch ts-node script in debug console",
      "program": "node_modules/.bin/ts-node",
      "args": ["index.ts"], // update entry point
      "request": "launch",
      "type": "node",
      "skipFiles": [
        "<node_internals>/**"
      ]
    },
    {
      "name": "Launch ts-node script in the terminal",
      "command": "ts-node index.ts", // update entrypoint
      "request": "launch",
      "type": "node-terminal",
      "skipFiles": [
        "<node_internals>/**"
      ]
    }
  ]
}
```

#### @babel/node

The following configuration will wrap the debugger around `babel-node` entry point.

```json
{
  "version": "0.2.0",
  "configurations": [
    // ...
    {
      "name": "Launch babel-node script in debug console",
      "program": "node_modules/.bin/babel-node",
      "args": ["src"], // update entry point
      "request": "launch",
      "type": "node",
      "skipFiles": [
        "<node_internals>/**"
      ]
    }
  ]
}
```

#### Nodemon

Add new configuration with Run &rarr; Add configuration option, select `Node.js: Nodemon Setup`.

Update `program` field to point to the nodemon executable package, and add arguments with `args` field to point to the entry point.

```json
{
  "version": "0.2.0",
  "configurations": [
    // ...
    {
      "name": "Launch nodemon script in debug console",
      "program": "node_modules/.bin/nodemon",
      "args": ["-r", "dotenv/config", "--exec", "babel-node", "src/index.js"], // update entry point
      "request": "launch",
      "type": "node",
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen",
      "restart": true,
      "skipFiles": [
        "<node_internals>/**"
      ]
    }
  ]
}
```

#### Mocha

Add a new configuration, and choose `Node.js: Mocha Tests` configuration.

Replace `tdd` with `bdd` as `u` parameter and `program` field to point to the mocha executable package.

```json
{
  "version": "0.2.0",
  "configurations": [
    // ...
    {
      "name": "Launch mocha tests",
      "program": "node_modules/.bin/mocha",
      "args": [
        "-u",
        "bdd",
        "--timeout",
        "999999",
        "--colors",
        "test"
      ],
      "request": "launch",
      "type": "node",
      "internalConsoleOptions": "openOnSessionStart",
      "skipFiles": [
        "<node_internals>/**"
      ]
    }
  ]
}
```

### Attach configs

`Auto Attach` should be activated in settings with the `With Flag` value. In that case, auto-attaching is done when `--inspect` flag is given.

The debugger should be attached when some of the following scripts are executed.

```json
{
  // ...
  "scripts": {
    // ...
    "start:debug": "node --inspect index.js", // update entry point
  }
}
```

##### Jest

```json
{
  // ...
  "scripts": {
    // ...
    "test:debug": "node --inspect -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
  }
}
```

##### NestJS

```json
{
  // ...
  "scripts": {
    // ...
    "start:debug": "nest start --debug --watch"
  }
}
```

### Debugging basics

During the debugging, the variables tab shows local variables. The `step over` option goes to the following statement in the codebase, while `step into` option goes deeper into the current statement.

Log points can add logs in debug console when a certain part of the codebase is executed without pausing the process.

### Boilerplate

Here is the [link](https://sevic.dev/nestjs-starter) to the boilerplate I use for the development.