Node.js debugging with Nuxt 3

As a PHP developer, I sometimes felt lost when debugging Nuxt 3 applications with node. I'm used to xDebug and step-by-step debugging and wanted to achieve the same thing with Nuxt 3.
If you want to learn how to drop the old-fashioned console.log(), you should read this blog post.

Debugging in node applications like Nuxt 3 is quite complex. Usually code is written in Typescript that needs a compilation to JS. Vue 3 components with the <script setup> syntax are also compiled into Javascript by the Vue 3 compiler.

In order to use breakpoint, we first have to enable and output sourcemaps. Sourcemaps are enabled for your server build by default, and for the client build in dev mode, but you can enable them more specifically in your configuration.

export default defineNuxtConfig({
  sourcemap: {
    server: true,
    client: true
  }

  // Alternative: sourcemap: true
})

The journey begins

Many tutorials and Stack Overflow posts will tell you to do something like this:
node --inspect=0.0.0.0 ./node_modules/nuxt/bin/nuxt.mjs dev, but this will not work. I spend countless hours, and you will end up not being able to set working breakpoints.

In sheer forgiveness, I created a Github issue in the VSCode JS Debug issue queue, and there I finally got some insight to why this does not work:

Connor Peet, the maintainer of the VSCode JS Debugger extension and Microsoft employee stated:

When the main process in Nuxt is --inspect'ed, it will spawn its child with --inspect. But that listens on port 9229 by default, and if that port is in use (by the parent process listening on the same port, for example), the inspector fails to bind and it cannot be debugged.

The code of the Nuxt CLI can be found here.

If you start the debugger as above, you can only debug the Nuxi CLI client, but your real application runs in child process. It is not possible to attach the debugger to the child process.

// Start new process
childProc = fork(globalThis.__nuxt_cli__?.entry!, ['_dev', ...rawArgs], {
  execArgv: [
    '--enable-source-maps',
    process.argv.includes('--inspect') && '--inspect',
  ].filter(Boolean) as string[],

But this code snipped highlights how Nuxt 3 expects the debugger to be started. If you pass a process argument called --inspect to nuxi it will automatically pass it to the Nuxt 3 application.

We can adapt your package.json like this:

"scripts": {
  "debug": "nuxi dev --inspect",
  ...
}

Unfortunately, this only works if you start your Nuxt 3 application locally. The next chapter explains how to debug a Nuxt 3 application in a container.

Debugging Nuxt3 in a Docker Container (exemplary with Lando)

In this example, I will use Lando to run the frontend container, but the procedure is the same for any Docker based setups.

Step 1: Expose the Node debugger port inside your container to the container host

In .lando.yml:

name: example
recipe: drupal10
config:
  via: nginx
  webroot: docroot
  php: '8.2'
  composer_version: 2-latest

services:
  frontend:
    type: node:18
    ssl: true
    sslExpose: false
    ports:
      - 28111:9229
    overrides:
      image: node:20

In this simple example, we start a frontend container that makes the port 9229 inside the container accessible from the host machine via port 28111 (randomly selected).

Step 2: Patching Nuxt 3 to allow access to the debugger

Sadly, at this point, you cannot attach the debugger via port 28111. The debugger is strictly bound to the IP 127.0.0.1 and not accessible via the port mapping.

Because of the way Nuxt 3 is passing the --inspect parameter to the child process described above, there is no way of changing the parameter to something like this: --inspect=0.0.0.0. The parameters configuring the IP and the port are partially documented here.

Until this is fixed in Nuxt 3, we have to patch Nuxi CLI to make debugging work in a container.

npm install patch-package
mkdir patches
cd patches
touch nuxi+3.10.0.patch

Put the following content into the nuxi+3.10.0.patch file:

diff --git a/node_modules/nuxi/dist/chunks/dev.mjs b/node_modules/nuxi/dist/chunks/dev.mjs
index f76e082..fa750b1 100644
--- a/node_modules/nuxi/dist/chunks/dev.mjs
+++ b/node_modules/nuxi/dist/chunks/dev.mjs
@@ -253,7 +253,7 @@ async function _startSubprocess(devProxy, rawArgs) {
     childProc = fork(globalThis.__nuxt_cli__?.entry, ["_dev", ...rawArgs], {
       execArgv: [
         "--enable-source-maps",
-        process.argv.includes("--inspect") && "--inspect"
+        process.argv.includes("--inspect") && "--inspect=0.0.0.0"
       ].filter(Boolean),
       env: {
         ...process.env,

Add the postinstall hook to your package.json:

"scripts": {
  ...
  "postinstall": "patch-package && nuxt prepare",
  ...
},

Run npm install to execute the patch.

Attach the debugger with PHPStorm / WebStorm

Now, you can attach the debugger to the running node process inside the container like this:

  • Add a new Debug Configuration
  • Select “Attach to Node.js / Chrome”
  • Configure the exposed port

Start your application with npm run debug, set a breakpoint, and then attach the debugger.

Enjoy the power of Node debugging!

Attach the debugger with VSCode

Create a .vcode/launch.json or extend it with the following configuration:

{
    "configurations": [
        {
            "address": "localhost",
            "name": "Attach to container",
            "port": 28111,
            "request": "attach",
            "type": "node",
            "localRoot": "${workspaceFolder}/frontend", // Optional, only needed if your project is not in the root folder
            "remoteRoot": "/app/frontend", // Optional, only needed if your project is in a custom location in the container
        },
    ],
}

The localRoot and the remoteRoot are only needed if you have some special subfolder mapping between the container and the localRoot. In this example, the frontend lives in a project subfolder. Remove it if you have a single Nuxt 3 installation.

Start your application with npm run debug, set a breakpoint, and then attach the debugger.

Enjoy the power of Node debugging!


Do you have a question, a comment, or just feeling inspired? Mention us or share this article on Mastodon, Twitter or LinkedIn.

Subscribe to blog updates using the RSS Feed.

Related services
Tags