Visual Debugging with ES6 and Node.js

Alon Salant
Good Eggs Product Team
6 min readOct 13, 2016

--

We’ve been working with Node.js for several years at Good Eggs and love the JavaScript ecosystem, particularly the advancement of the language itself with ECMAScript 6.

While the number of options in the JavaScript ecosystem is still exploding, it does feel like we are starting to narrow in on a great toolset. Today, all our new applications are using ES6 and ReactJS and it feels right. Below I share my experiences adding a valuable tool to the toolset — a visual debugger.

Please Use a Visual Debugger

As software developers we spend a large percentage of our time trying to figure out why something isn’t working the way we think it should. If you do not have a visual debugger in your everyday toolkit you are wasting your time with console logging. You should start using one. If one is not easily available, you should set it as a goal to find and start using one.

If you ever console.log to inspect the state of your test or running app, you are taking too much time to see what is going on. You rarely get the info you need the first time. So you edit your code to add more temporary log statements that you will later remove, rerun your code or wait for your server to restart. Not efficient. Much better to set a breakpoint, run your code and then poke around as much as you need until you answer your question.

Visual Debugging in Node.js

One thing I have missed from past work in other languages is great visual debugging tools, particularly on the server. I’ve used console.log more than I like to admit, but I’ve held out hope for a better day. I’ve checked in on the options for JavaScript periodically but never adopted a visual debugger as I didn’t find them sufficiently smooth in my workflow when transpiling from CoffeeScript or ES6.

I checked in again as part of a hack day we recently hosted focused on developer productivity. I was pleased to find that things are looking pretty great today. I won’t get into the toolset for ReactJS and will focus on what I learned about visual debugging for ES6 in Node. Feel free to share feedback or tips of your own in the comments. I’ll update this post with anything useful.

Criteria

  • Be able to run a visual debugger for a running Node.js web application using ES6 with BabelJS, breakpointing on source files — not the transpiled JavaScript.
  • Be able to run a visual debugger for a Mocha unit test. I find this to be more useful in a normal workflow than debugging a running app.
  • Visual debugger should support breakpoints, inspections, watches and mutating the state of running app.
  • It should start up quickly and be stable.

Node Inspector

I first wanted a solution that any developer could use regardless of their setup (i.e. editor). node-inspector has been around since 2010 and continues to be the go-to for debugging Node.js.

To debug with node-inspector you:

  1. Start your app with the debugger enabled
  2. Start the node-inspector server
  3. Open the node-inspector UI in Chrome
  4. Use it almost exactly as you would use the Chrome Developer Tools to debug client-side

For convenience, node-inspector provides node-debug which takes care of steps 1–3 above for you. Here’s the best way I found to debug an ES6 app with node-inspector’s node-debug.

node-debug --debug-port 9041 \
--nodejs node_modules/babel-cli/bin/babel-node \
--hidden node_modules/ \
--no-preload web/index.js

What’s going on here?

--debug-port is optional but is useful if you are going to be doing this a lot. The node debugger defaults to port 5858 so you’ll have conflicts if you want to have multiple apps running with the debugger enabled.

--nodejs specifies arguments to be passed to the underlying node process — equivalent to running node node_modules/bable-cli/bin/babel-node which is equivalent to running babel-node when you have the babel CLI installed. This is the key to running node-inspector with ES6 and should be useful when using other transpilers.

--hidden tells node-inspector not to look for breakpoints in files that match the argument. This was the option that made the biggest difference in speeding up the startup of apps with the debugger enabled. There are a few formats for providing this argument detailed in the node-inspector docs. As written above, it evaluates as a regular expression so all paths that include node_modules/ will be ignored.

--no-preload tells node-inspector not to scan for .js files. With this option, when you load the debugger you will not see all files in your app until they are actually loaded by the process. This is another performance optimization but I did not see as dramatic an effects as with --hidden.

To debug a Mocha unit test:

node-debug --debug-port=9041 \
--nodejs node_modules/babel-cli/bin/babel-node \
--hidden node_modules/ \
--no-preload \
./node_modules/.bin/_mocha --no-timeouts test.js

Everything is the same as when running an application except for the last line. Here we are running Mocha directly with node and passing two arguments to it.

--no-timeouts disables the behavior in Mocha of failing a test if it does not complete quickly enough. The timeout defaults to 2000ms but you can override it in a test. However, if you are sitting on a breakpoint scratching your head about the state of your app, you’ll cross the timeout threshold and your test will fail. Not helpful so disable it.

We’ve captured these commands as npm scripts but you can do the same in whatever task runner you are using.

...
},
“scripts”: {
“dev:debug”: “node-debug --debug-port=9041 --nodejs node_modules/babel-cli/bin/babel-node --hidden node_modules/ --no-preload”,
“test:debug”: “NODE_ENV=test node-debug --debug-port=9041 --nodejs node_modules/babel-cli/bin/babel-node --hidden node_modules/ --no-preload ./node_modules/.bin/_mocha --no-timeouts”,
...
},
...

Which can then be run as:

$ npm run dev:debug web/index.js
$ npm run test:debug test.js

Issues

The only issue I ran into is that in the debugger your source files show up with a .js.source extension and the transpiled result with .js. Babel appears to be doing something under the covers to create source maps and link them from the transpiled files. Pretty cool that you don’t have to worry about source maps at all yourself and not too bad to remember that the .js.source files are the ones on which to set your breakpoints.

JetBrains WebStorm

Before working in Node.js I worked in Ruby/Rails and before that in Java. I thought JetBrains did a great job with IntelliJ for Java and continued using their tools with RubyMine for Ruby/Rails. Their seamless integration of unit testing and debugging along with first-class support for standard refactors made them efficient for my workflow.

WebStorm is JetBrains’ JavaScript IDE for client and server-side development. Debugging ES6 in a running app or unit test in WebStorm turned out to be very smooth.

To debug an app:

  • Create a new Node.js Run/Debug Configuration
  • Set Node Interpreter to the path to node_modules/.bin/babel-node
  • Save and click Debug

It just works. Set breakpoints on your source files and inspect your running app.

To debug a Mocha test:

  • Edit the default Mocha Run/Debug Configuration
  • Set Node Interpreter to the path to node_modules/.bin/babel-node

Set breakpoints in your test or code-under-test then open your test and click Debug.

Wow. It just works. Happy debugging!

If you liked this, please click the 💚 below so others will find it on Medium.

Good Eggs connects people who love food, directly with people who make it. We deliver the most incredible food, straight to Bay Area homes. If you are inspired by our mission is to grow and sustain local food systems worldwide, find out how you can help.

--

--

Software developer and entrepreneur using my passion and skills to benefit personal health, family, community, and our planet.