Stanley Ulili I'm a freelance web developer and researcher from Malawi. I love learning new things, and writing helps me understand and solidify concepts. I hope by sharing my experience, others can learn something from them.

Exploring competitive features in Node.js v18 and v19

6 min read 1814

Exploring competitive features in Node.js v18 and v19

Node.js has been a popular JavaScript runtime since its release in 2009. But the advent of two new runtimes, Deno and Bun, has brought a lot of hype for the new features they present in contrast to Node.

From afar, it may seem like Node.js is stagnating and nothing exciting is happening — but the reality is different. Two recent Node.js releases, v18 and v19, came with a lot of significant features:

  • Experimental support for browser APIs, such as Fetch and the web streams API
  • An experimental, inbuilt test runner
  • Support for the recent version of Chromium’s V8 engine
  • Experimental support for watch mode, which replaces a tool like nodemon

In this tutorial, we will explore the following cool new features in Node.js v18 and v19:

Node.js v18 features

Node.js v18 was released on April 19, 2022, and became a current release through October 2022, when Node.js v19 was released. A current release means that the version gains non-breaking features from a newer version of Node.js.

Node.js v18 gained the watch mode feature, which was backported in Node v18 when v19 was released. On October 25, 2022, Node.js v18 was promoted to LTS (long-term support) and will continue receiving support until 2025.

The following are some of the features that are available in Node.js v18.

Inbuilt Fetch API

Before Node.js v18, you couldn’t use the Fetch API in Node.js to request data from an API. You had to install node-fetch, Axios, or other third-party packages, With Node.js v18, you no longer need to install either package thanks to v18’s experimental Fetch API, which is available globally.

Let’s look at how to use the Fetch API in Node.js v18. First, create a getData.js file and add the following function that sends a request to an API:

async function fetchData() {
  const response = await fetch(
  if (response.ok) {
    const data = await response.json();


Save the file contents, then run the file with the node command:

node getData.js

When the command runs, the output will look like the following:

(node:29835) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
  id: 6638,
  uid: '75026571-e272-4298-b2c0-c3e9e6363437',
  name: 'Candy Kane',
  prefix: 'Rep.',
  initials: 'LBS'

In the output, Node.js logs a warning that the Fetch API is experimental. After the warning, we see the JSON data that the API returned.

Inbuilt test runner module

Developers typically use unit testing to test software components. From the early releases of Node.js, we could write simple tests with the assert library. But as our tests grew larger, so did our need to organize tests and write descriptive messages.

As a solution, test runners such as Jest, Jasmine, and Mocha emerged, and have been the go-to tools for unit testing.

With the release of Node.js v18, a test runner is now included in Node.js and can be accessed with:

import test from 'node:test';

Note that we are using the node: scheme to import the module. You can also use CommonJS:

const test = require('node:test')

Let’s learn how to use it. First, initialize npm with the following:

npm init -y

In your package.json file, enable the ES modules:

  "license": "ISC"
  "type": "module",

Next, create a math.js file and add a function that returns the result of adding two numbers:

const sum = (a, b) => {
  return a + b;

export default sum;

To test the function with the Node.js test runner, create a test.js file with the following content:

import test from "node:test";
import assert from "assert/strict";
import sum from "./math.js";

test("Sum function", async (t) => {
  await t.test("It should add two numbers", () => {
    assert.equal(sum(2, 2), 4);
  await t.test("It should not subtract numbers", () => {
    assert.notEqual(sum(3, 2), 1);

In the first line, we import the test runner. In the second line, we import the assert library, and subsequently, the sum() function in the math.js file.

After that, we create a test case that has two subtests, which test if the sum() function works properly.

Now, run the tests:

node test.js

Your output will look like the following:

TAP version 13
# Subtest: Sum function
    # Subtest: It should add two numbers
    ok 1 - It should add two numbers
      duration_ms: 1.171389
    # Subtest: It should not subtract numbers
    ok 2 - It should not subtract numbers
      duration_ms: 0.279246
ok 1 - Sum function
  duration_ms: 5.522232
# tests 1
# pass 1
# fail 0
# cancelled 0
# skipped 0
# todo 0

In the output, we can see that Node.js has description messages of the tests that run.

Web Streams API support

The Web Streams API is an experimental feature in Node.js that lets you break a large file, like a video or text file, into smaller chunks that can be consumed gradually. This helps avoid memory issues. In older versions of Node.js, you could use Node.js streams to consume large files. But this functionality wasn’t available for JavaScript apps in the browser. Later, WHATWG defined the Web Streams API, which has now become the standard for streaming data in JavaScript apps.

Node.js didn’t support this API until v18. With v18, all of the Streams API objects, such as ReadableStream, WritableStream, and TransformStream, are available. To learn more about how to use the Streams API, check out the documentation.

Building binaries with the snapshot feature

Another exciting feature is the ability to build a single-executable Node.js binary. Before Node.js v18, the only way to build a Node.js binary was to use a third-party package, like pkg.

But now, you can make use of the experimental snapshot flag --node-snapshot-main to build a binary. For more details on how this feature works, see this tutorial.

V8 engine upgraded to v10.1

Node.js is built on top of the V8 engine, created by Google and maintained for Chromium to execute JavaScript. With each release, it introduces new features and some performance improvements, which end up in Node.js.

Google released V8 10.1, which introduced some new array methods, such as findLast() and findLastIndex(), as well as Intl.supportedValuesOf(code). The V8 engine also added new methods to the Intl.Locale API, and optimized the class fields and private methods.

watch mode and other Node.js v19 features

Node.js v19 was released on October 18, 2022. Since 19 is an odd number, it will never be promoted to LTS, but will continue receiving support until April 2023, when a new, even-numbered Node.js version is released.

While Node.js v19 has not released a lot of features in comparison to Node.js v18, it has shipped one of the most requested features to past Node versions as well: watch mode.

When you create and start a server in Node.js, then later make changes to the file, Node.js doesn’t pick up the new changes automatically. You either need to restart the server or use a tool like nodemon, which automatically reruns a file when it detects new changes.

With the release of Node.js v19, this is no longer necessary. Node v19, as well as Node ≥ v 18.11.0, is now able to automatically restart a process when it detects new changes using the node --watch feature, which is currently experimental.

To run a file in watch mode, use the --watch flag:

node --watch index.js

When you edit the index.js file, you will see that the process automatically restarts and the new changes are reflected without stopping the server.

As mentioned, this feature has also been backported to Node.js ≥ v18.11.0, which means you don’t have to use Node.js v19 if this is the only feature you need.

HTTP(S)/1.1 KeepAliveby default

Node.js uses an http.globalAgent for outgoing HTTP connections and https.globalAgent for outgoing HTTPS connections. These agents ensure TCP connection persistence as well as that HTTP clients can reuse the connections for multiple requests.

You can configure the agents to reuse connections by setting the HTTP 1.1 keepAlive option to true; otherwise, set it to false to avoid reusing connections, which makes things slower.

For Node.js version ≤18, outgoing connections for HTTP/HTTPS have the keepAlive option set to fal``se, so connections are not reused for multiple requests, leading to slower performance. With Node.js v19, the keepAlive option is now set to true, which means your outgoing connections will be faster without doing any configurations.

Let’s verify this. Assuming you are using nvm, you can install Node.js ≤ v18 and temporarily switch to it:

nvm install v18.12.1
node -v
// Output
// v18.12.1

Create a checkHttpAlive.js file and add the following code to inspect the http.globalAgent:

const http = require('node:http');

Your output will look as follows:

// Output
Agent {
  keepAliveMsecs: 1000,
  keepAlive: false,  // this is the keepAlive option

In the output, you will notice that keepAlive is set to false by default on Node v18.

Let’s compare it with Node.js v19. Switch the Node.js version to v19 with nvm:

nvm install v19.0.1
node -v
// output:
// v19.0.1

Run the checkHttpAlive.js file again:

node checkHttpAlive.js

The output will match the following:

// output
Agent {
  keepAliveMsecs: 1000,
  keepAlive: true,

In the output, you can see the keepAlive option is set to true by default in Node.js v19.

V8 engine upgrade to 10.7

The V8 Engine for Node.js v19 has been upgraded to version 10.7. It did not ship with a lot of features — it only added the Intl.NumberFormat feature to the JavaScript API.

The Intl.NumberFormat internationalizes a number as a currency. An example:

> new Intl.NumberFormat('en-US', { style: 'currency', currency: 'GBP' }).format(3392.10)
'£3,392.10'     // output


In this article, we explored cool features in Node.js v18 and v19. First, we looked at the new features in v18, which include the inbuilt Fetch API, a new test runner and snapshot feature, watch mode, and support for the Web Streams API. We then looked at new features in Node v19, which includes watch mode, and the HTTP 1.1 keepAlive feature.

More great articles from LogRocket:

As exciting as the new Node.js features are, most of these features already exist in Bun and Deno. The runtimes also include useful features, such as native TypeScript support, web sockets API, and execute faster than Node.js.

If you are not sure which Node.js version to use, I would recommend v18. Its support will last until 2025, unlike Node v19, whose support will end next year. If you want to learn about these features in more depth, refer to the documentation page.

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. .
Stanley Ulili I'm a freelance web developer and researcher from Malawi. I love learning new things, and writing helps me understand and solidify concepts. I hope by sharing my experience, others can learn something from them.

2 Replies to “Exploring competitive features in Node.js v18 and v19”

  1. “Before Node.js v18, you had to install node-fetch or Axios to request a resource from a server” this is simply wrong 🙁

Leave a Reply