Node.js 22: The future of server-side JavaScript

NearformNearform
6 min read

By Marco Ippolito

Discover Node.js 22’s exciting new features and improvements

With the release of version 22, Node.js continues to push boundaries and offer developers new tools and enhancements to create powerful and efficient applications. In this blog post, we'll explore some of the exciting features and improvements introduced in Node.js 22.

V8 gets updated to version 12.4

This Node.js version includes an important update of V8 to version 12.4, packed with new features and improvements. Here's what's included in this update:

WebAssembly Garbage Collection

One of the highlights of this release is the introduction of WebAssembly Garbage Collection. This feature enhances memory management for WebAssembly code, leading to improved performance and efficiency in your applications.

Array.fromAsync

Introducing Array.fromAsync, a new method that allows you to asynchronously create arrays from async iterables. Here's a quick example of how you can use it:

async function* asyncIterable() {
  for (let i = 0; i < 5; i++) {
    await new Promise((resolve) => setTimeout(resolve, 10 * i));
    yield i;
  }
}

const array = Array.fromAsync(asyncIterable);
// [0, 1, 2, 3, 4]

Sets method

Several new methods are added to Sets, including union, intersection, and difference. These methods make it easier to manipulate Sets and perform common operations. Check out the following examples:

const lts = new Set([18, 20, 22]);
const nonLts = new Set([17, 19, 21]);

const all = lts.union(nonLts); // all versions [18, 20, 22, 17, 19, 21]

all.intersection(nonLts); // only values contained in both sets [17, 19, 21]

all.difference(nonLts); // only values not contained in both sets [18, 20, 22]

Iterator helpers

These helpers include methods like take, drop, and map, making it easier to manipulate and process iterator values. Here are some examples:

function* fibonacci() {
  let current = 1;
  let next = 1;
  while (true) {
    yield current;
    [current, next] = [next, current + next];
  }
}

// Prints only the first 5 values
for (const n of fibonacci().take(5)) {
   console.log(n);
}

// Takes the first five elements, then drops the first two
for (const n of fibonacci().take(5).drop(2)) {
  console.log(n);
}

// Elevates every number at power of 2
for (const n of fibonacci().map((x) => x ** 2)) {
  console.log(n);
}

require()ing ESM modules

One of the most anticipated features in Node.js 22 is the ability to require ECMAScript modules (ESM) synchronously. With the --experimental-require-module flag, developers can now load ESM modules using require(). This feature addresses a common pain point for developers transitioning from CommonJS to ESM. Package authors can now gradually migrate to ESM without disrupting the user experience or bloating the node_modules directory with duplicated modules.

Exporting a ESM module:

// sum.mjs
export default function sum(a, b) { return a + b }

A ESM module can now be used in a CommonJS module:

// index.cjs
const sum = require('./sum.mjs')
console.log(sum(1,2)) // it works!
node --experimental-require-module index.cjs

It's worth noting that while synchronous ESM graphs offer significant benefits, there are still some edge cases and feature interactions to consider. For example, interactions with other experimental features or scenarios involving cyclic dependencies may require further refinement. However, as an initial iteration of an experimental feature, Node.js 22 lays the groundwork for future improvements and iterations.

WebSocket client

In Node.js 22 the WebSocket client implementation, exported from Undici, is now enabled by default, eliminating the need for external dependencies. This built-in support for WebSockets simplifies the development process and ensures compatibility with modern web standards.

// Create WebSocket connection
const socket = new WebSocket("ws://localhost:8080");

// Connection opened
socket.addEventListener("open", (event) => {
  socket.send("Hello Server!");
});

Package.json script execution

With the new --run flag, developers can now execute scripts directly from the package.json. This experimental feature simplifies the execution of common tasks and workflows defined in the project's package.json, improving the development experience.

The --run flag allows running a specified command from a package.json's "scripts" property. If no specific "command" is provided, it will list all available scripts.

When using --run, node prepends ./node_modules/.bin, relative to the current working directory, to the PATH. This enables the execution of binaries from dependencies.

To execute the test script from package.json in the current folder, simply run:

node --run test

You can also pass arguments to the command. Any argument after -- will be appended to the script:

node --run test -- --verbose

It's important to note that node --run is not designed to replicate the behaviours of npm run or similar commands from other package managers. Instead, it prioritises top performance for common use cases by intentionally limiting certain functionalities:

  • It doesn't search for package.json files outside the current folder.

  • The .bin or node_modules/.bin paths of folders outside the current folder are not prepended.

  • pre or post scripts are not executed alongside the specified script.

  • Package manager-specific environment variables cannot be defined.

Stable Watch Mode

Watch Mode, previously an experimental feature, is now considered stable.

For those unfamiliar, Watch Mode is a feature that automatically restarts the Node.js process whenever changes are detected in watched files. This means no more manual restarts or interruptions during development and testing, simply add the --watch flag when starting your Node.js application.

By default, it monitors the entry point file and any required or imported modules. However, you can customise this behaviour using the --watch-path flag to specify specific paths to watch.

For example:

node --watch index.js

And if you want to specify custom paths:

node --watch-path=./src --watch-path=./tests index.js

It's important to note that --watch-path is currently supported only on macOS and Windows. Attempting to use it on unsupported platforms will result in an exception.

Glob and GlobSync

This release introduces glob and globSync functions for pattern matching. Developers can now utilise these functions for matching file paths based on specified patterns.

import { glob } from 'node:fs';

glob('**/*.js', (err, matches) => {
  if (err) throw err;
  console.log(matches);
});

JavaScript Copy to clipboard

import { globSync } from 'node:fs';

console.log(globSync('**/*.js'));

Improved performance with Maglev

The introduction of V8's Maglev Compiler, enabled by default on supported architectures, brings significant performance improvements to short-lived CLI programs.

Maglev follows TurboFan and SparkPlug as the third compiler in the V8 engine's lineup.

TurboFan produces highly optimised machine code, albeit with a longer compilation time compared to SparkPlug, which prioritises compilation speed over optimisation. However, Maglev bridges this gap by generating code that is not only significantly faster than Sparkplug's but also compiled much more swiftly than TurboFan's.

By optimising CPU usage, Maglev enhances the overall efficiency of Node.js applications, resulting in faster execution times and better resource utilisation.

Stream default High Water Mark

In Node.js 22, the default High Water Mark for streams is increased from 16KiB to 64KiB, resulting in a noticeable performance boost. While this change might use a bit more memory, the improvements in speed and efficiency outweigh any downsides, especially for applications handling large amounts of data. This update reflects the increased capabilities of modern machines since Node.js creation, following years of progress and technological advancements.

Deprecations and removals

As part of ongoing maintenance and improvement efforts, Node.js 22 also includes several deprecations and removals aimed at streamlining the codebase and promoting best practices. Notably, a lot of crypto constructors that were not supposed to be public and old util methods have been removed. Developers are encouraged to review the list of deprecations and plan accordingly to ensure compatibility with future releases of Node.js.

Conclusion

Node.js 22 represents another significant milestone in the evolution of the Node.js platform, introducing new features, performance enhancements, and improvements to the development experience.

We encourage you to explore the latest release of Node.js 22 and try these new features.

As always, feedback and contributions from the community play a crucial role in shaping the future of Node.js, so don't hesitate to get involved and share your thoughts and ideas.

1
Subscribe to my newsletter

Read articles from Nearform directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nearform
Nearform