Brian De Sousa Geek. Dad. Husband. Developer. Traveler.

Node.js 15: What’s new and how the developer experience has improved

5 min read 1604

The next major release of Node.js is now available. So far in the series of Node.js 15 releases, we’ve seen several improvements, some new JavaScript language features, and even some breaking changes.

Some of these improvements, like the new version of NPM, are substantial and dramatically improve the developer experience. Other changes, like the new version of the N-API or experimental support for the QUIC protocol, may not appeal to the masses but are just as important to ensure that Node.js continues to be extensible and prepared for the future of the web.

Let’s take a look at what is new and why you should care.

Tip: Node.js 15 is not a long-term support (LTS) release. Support for this release is slated to end in June 2021, shortly after the first Node.js 16 release comes out. You may want to consider keeping your production applications on Node.js 14 for the time being.


The inclusion of NPM 7 is likely the biggest headliner of this release, thanks to a bevy of new features. The biggest feature is NPM workspaces, which is the start of built-in support for creating and managing multiple NPM packages in a single file system. Developers who are already actively using the Yarn package manager or Lerna should see similarities with NPM’s workspace implementation.

It is relatively easy to get started with NPM workspaces. First, arrange all your NPM package folders on the file system, create a top-level package.json that refers to each of these folders, and start running commands across all NPM packages.

NPM7 workspaces
Here’s an NPM workspace example.

Keep in mind that this is the initial implementation of workspaces in NPM. If you are seasoned Yarn or Lerna user, you will notice that many table-stakes capabilities are still missing. For example, being able to execute scripts within specific packages in the workspace is not yet possible using the NPM CLI. I’m sure we’ll see workspace capabilities expand swiftly.

Tip: You can run a script in a specific package in a workspace using the --prefix argument. For example, npm run --``prefix recipe-generator test will run the test script in the recipe-generator package in the current workspace.

In addition to workspaces, there are several other major changes introduced in NPM 7.

Peer dependencies were essentially ignored in NPM 6. In NPM 7, they are installed by default. You can read more about this change in NPM RFC0025. Interestingly enough, the React developer community may have influenced this change.

Running NPM packages without installing them first has become part of core NPM with the new npm exec command. This new command replaces npx but also behaves slightly differently. npx was rewritten to use npm exec but the same CLI for backward compatibility. You can read more about this in the NPM documentation.

NPM now supports yarn.lock files. If present, this file will be used by NPM to determine where to fetch packages from. NPM will even update it as packages are added or removed. There is no plan to replace package-lock.json with yarn.lock. In fact, a package-lock.json file is still generated and maintained as the authoritative source of package version metadata.

NPM has a new package-lock.json format that now supports deterministic builds. In simple terms, this means that NPM ensures that versions of installed packages remain the same across builds if the package-lock.json file has not changed. Deterministic builds have been a feature of Yarn for quite some time.

This only scratches the surface of NPM 7. You can read more about what is new in NPM 7 here.

V8 8.6

Node.js 15 bumps the version of the V8 engine it uses from 8.4 to 8.6. V8 is the underlying JavaScript engine that Node.js runs atop.

So why should you care? Because V8 defines the JavaScript language features that are available to developers. While this version bump is small, there are a few new language features that will reduce the amount of code you need to write and potentially make your code more readable.

First off, there are some new logical assignment operators to allow you to assign a value to a variable conditionally:

// new logical AND operator
let a = 1;
let b = 0;
a &&= 2;
b &&= 2;
console.log(a); // prints 2
console.log(b); // prints 0

// new logical OR operator
a = 1;
b = null;
a ||= 3;
b ||= 3;
console.log(a); // prints 1
console.log(b); // prints 3

// new logical nullish operator
a = 1;
b = null;
a ??= 4;
b ??= 4; 
console.log(a); // prints 1
console.log(b); // prints 4

The Promise object has a new any() function that accepts one or more promises and returns the promise that resolves first. Rejected promises are ignored. If all promises are rejected, a new AggregateError is returned containing the errors from all the rejected promises.

Many developers do not have regular expression syntax committed to their brains. Luckily, the String object now has a new replaceAll() function, which means there is one less regular expression that you need to look up to replace all occurrences of a string.

It’s as simple as: myString.replaceAll(``'``regex``'``, '``function``'``).

Experimental support for the QUIC protocol (HTTP/3)

The HTTP/1 standard appeared around 1996. Nearly 19 years later and after many extensions to the original standard, HTTP/2 became standard in 2015.

Now we are hot on the heels of HTTP/3, which most notably will replace the network transport layer protocol from TCP to QUIC. Node.js continues to show a commitment to the developer community to stay ahead of the latest standards by adding experimental support for the QUIC protocol.

Okay, standards are evolving at an even quicker pace, but what does this mean to the mainstream Node.js developer? Well, it could mean a lot depending on what you are building.

If you are using the net module to create sockets in your application, you could start taking advantage of a new set of experimental functions and objects to establish connections using the QUIC protocol. QUIC promises to reduce latency compared to TCP, TLS encryption is baked in, and more.

Unhandled rejected promises

The biggest breaking change in Node.js 15 is how unhandled rejected promises are handled. In Node.js 14, if you did not explicitly handle a rejected promise, you would see this warning:

(node:764) UnhandledPromiseRejectionWarning: something happened
(Use `node --trace-warnings ...` to show where the warning was created)
(node:764) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see (rejection id: 2)
(node:764) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

The warning has now been replaced in Node.js 15 with an error:

          triggerUncaughtException(err, true /* fromPromise */);

[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "recipe 
could not be generated".] {

This change has the potential to impact many applications, especially for developers who ignored warning messages in the past. You can revert this behavior by starting Node.js with the new --unhandled-rejections=warn argument.

Experimental diagnostics channel module

Node.js 15.1.0 introduces a new experimental module named diagnostics_channel. This module essentially enables a publish-subscribe pattern that developers can use to publish arbitrary data to a channel that can be consumed by other modules or applications. The module is intentionally generic and could be used in a variety of ways.

You can import and start using the module right away:

const diagnostics_channel = require('diagnostics_channel');

Creating a channel and publishing a message to it is actually very simple. This example demonstrates creating a channel and publishing a message to it:

const getData = filter => {
    const alertChannel ='my-module.alert');

    return new Promise((resolve, reject) => {
        const data = doSomething(filter);
        if (data) {
        } else {
            const errorMessage = "data could not be retrieved";

Consuming messages from the channel is just as easy. Here’s an example of how you can subscribe to a channel from another module or application and make use of the data received from the channel:

  const alertChannel ='my-module.alert');
  alertChannel.subscribe(message => {
    console.log(`Alert received on my-module.alert channel: ${message}`);

Other notable changes

There are a few other notable changes in the release that likely will not impact as many developers compared to what we have seen so far:

  • N-API 7 is here. N-API is one of several ways that developers can build native add-ons for Node.js. N-API versions are back-ported to older LTS releases as far back as Node.js 6. The N-API ensures addons remain compatible with past, present, and future Node.js LTS releases
  • The deprecated node debug command has now been removed. It is replaced with node inspect, which uses the widely adopted inspector-based approach to debugging Node.js
  • Several deprecated functions in the repl module have been removed. Removal of deprecated functions should not come as a surprise, but it might be worth reviewing the release notes if you are using the repl module in your applications

What’s next?

We can expect more Node.js 15 minor and patch releases until April 2021. After that, we’ll start to see the first few iterations of Node.js 16 which will be the next LTS release.

200’s only Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket.

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. .
Brian De Sousa Geek. Dad. Husband. Developer. Traveler.

Leave a Reply