NashTech Blog

Node.js 22: A Detailed Overview of New Features

Table of Contents

Node.js 22 is about to enter Long-Term Support (LTS) this October, bringing several exciting features that enhance both performance and developer experience. From easier handling of ECMAScript modules (ESM) to new Set methods and built-in WebSocket clients, this release packs many improvements that will help Node.js developers streamline their workflows and improve their applications.

Let’s dive into the details of what’s new in Node.js 22, along with examples and insights into how these changes can benefit your projects.

Notable Changes

Synchronously require() ESM Modules

One of the most highly anticipated features of Node.js 22 is the ability to load ECMAScript modules (ESM) using the require() function—synchronously! Up until now, developers working with Node.js were forced to deal with asynchronous imports for ESM, making the transition from CommonJS to ESM trickier for many existing projects. But with the introduction of the –experimental-require-module flag, you can now import ESM modules synchronously, just like you would with CommonJS modules.

Why is this important?

The ability to require() ESM modules synchronously is a big deal because many developers still rely on the CommonJS module system, especially in legacy applications. Having to asynchronously import ESM modules created pain points when trying to migrate to ESM or work in mixed environments. This feature simplifies that transition, allowing developers to incrementally adopt ESM without having to completely rewrite their existing module imports.

Example:

Let’s say you have a module esm.mjs that fetches data from an external server. Your main entry point, index.cjs, is a CommonJS file, and you want to import the ESM module:

// esm.mjs
export default async function getData() {
  try {
    const res = await fetch('<https://jsonplaceholder.typicode.com/todos/>');
    const data = await res.json();
    return data;
  } catch (error) {
    console.log(error);
  }
}

You can now require() the ESM module in your CommonJS entry point:

// index.cjs
const getData = require('./esm.mjs');

(async () => {
  const data = await getData.default();
  console.log(data);
})();

To make this work, simply run your application with the flag:

node --experimental-require-module index.cjs

With this new feature, you can gradually refactor parts of your application to ESM while continuing to use require() for synchronous imports.

V8 Engine Update: Version 12.4

Node.js 22 includes an upgrade to the V8 JavaScript engine, bringing it up to version 12.4. This update comes with several new JavaScript features and performance improvements. Here’s a look at some key additions:

Array.fromAsync()

The new Array.fromAsync() method allows you to create an array from an asynchronous iterable or promise. This is particularly useful when you need to process asynchronous data generation and transformation efficiently.

async function* asyncFetchTodos() {
  for (let i = 1; i <= 10; i++) {
    yield i;
  }
}

async function fetchTodo(id) {
  const res = await fetch('https://jsonplaceholder.typicode.com/todos/' + id);
  const data = await res.json();
  return data;
}

Array.fromAsync(
  asyncFetchTodos(),
  (id) => fetchTodo(id).then(todo => console.log(todo)),
);

In this example, we generate asynchronous tasks (asyncFetchTodos()), and Array.fromAsync() allows us to iterate over the asynchronous generator and fetch data for each task, logging the results as they are processed.

This can improve how you handle asynchronous data in arrays, offering a more readable approach to working with promises and asynchronous iterators.

WebAssembly Garbage Collection

The update to V8 12.4 also includes experimental support for garbage collection in WebAssembly. This improvement allows WebAssembly to manage memory more efficiently, which is critical for long-running, performance-intensive applications that use WebAssembly in Node.js.

Refer: https://developer.chrome.com/blog/wasmgc

New Set Methods for Easier Operations

Node.js 22 introduces new methods for the Set object. Previously, developers had to manually write complex code to perform operations like union, intersection, and difference on sets. Now, several new methods are available, making set operations significantly easier to handle.

.union()

The .union() method returns a new set containing elements from either or both of two sets.

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

const all = lts.union(nonLts);
console.log(all);  // Set(6) { 18, 20, 22, 17, 19, 21 }

This method is particularly useful when you need to merge sets in scenarios like combining version lists, collections, or datasets.

.intersection()

The .intersection() method returns a new set containing elements that are present in both sets.

const commonVersions = lts.intersection(nonLts);
console.log(commonVersions);  // Set(0) {}

This method can be helpful when you need to find common elements between two sets of data, such as comparing user roles or permission sets.

.difference()

This method returns a new set with elements present in one set but not in the other.

const uniqueVersions = lts.difference(nonLts);
console.log(uniqueVersions);  // Set(3) { 18, 20, 22 }

.isDisjointFrom()

The isDisjointFrom() method of Set instances takes a set and returns a boolean indicating if this set has no elements in common with the given set.

const a = new Set([1]);
const b = new Set([1]);
const isDisjointFrom = a.isDisjointFrom(b);
console.log(isDisjointFrom);
// false

.isSubsetOf()

The isSubsetOf() method of Set instances takes a set and returns a boolean indicating if this set has no elements in common with the given set.

const element = new Set(['a', 'b', 'c']);
const parent = new Set(['a', 'b', 'd', 'c']);
const isSubsetOf = element.isSubsetOf(parent);
console.log(isSubsetOf);
// true

.isSupersetOf()

The isSupersetOf() method of Set instances takes a set and returns a boolean indicating if all elements of the given set are in this set.

const element = new Set(['a', 'b', 'c']);
const parent = new Set(['a', 'b', 'd', 'c']);
const isSupersetOf = parent.isSupersetOf(element);
console.log(isSupersetOf);
// true

.symmetricDifference()

The symmetricDifference() method of Set instances takes a set and returns a new set containing elements which are in either this set or the given set, but not in both.

const classA = new Set(['Steven', 'Mike', 'Will']);
const classB = new Set(['Michale', 'Steven', 'Obama']);
console.log(classA.symmetricDifference(classB));
// Set(4) { 'Mike', 'Will', 'Michale', 'Obama' }

These new methods simplify set operations, reducing the amount of boilerplate code needed to perform common tasks.

Built-in WebSocket Client

With Node.js 22, the browser-compatible WebSocket client has been enabled by default, without the need for external libraries like ws. The WebSocket client, previously behind the –experimental-websocket flag, can now be used out of the box.

Example:

const ws = new WebSocket('ws://localhost:8080');

ws.addEventListener('open', () => {
  console.log("WebSocket connection established");
});

ws.addEventListener('message', (event) => {
  console.log("Message received from server: ", event.data);
});

This built-in WebSocket client makes real-time communication easier in Node.js applications, especially for use cases like chat applications, live updates, and event-driven systems.

New –run Flag for package.json Scripts

The new –run flag in Node.js 22 allows developers to execute scripts defined in package.json directly from Node.js, without relying on a package manager like npm or Yarn. This experimental feature simplifies the execution of common tasks and improves the developer experience by allowing direct execution of scripts.

Example:

node --run test

The –run flag offers performance improvements by minimizing certain functionalities such as environment variable configuration or pre/post hooks. This is ideal for scenarios where you want faster script execution without the overhead of a package manager.

Stable Watch Mode

Watch Mode is now stable in Node.js 22. This mode automatically restarts the Node.js process when changes are detected in watched files, making it ideal for development environments.

Example:

"scripts": {
	"start": "node server.js",
	"dev": "node --watch server.js"
},

Developers no longer need third-party tools like nodemon for file watching during development, as Watch Mode is now baked into Node.js itself.

Stream Default High Water Mark Increase

The default highWaterMark for streams has been increased from 16KiB to 64KiB in Node.js 22. This means that streams now have a larger internal buffer, improving performance at the cost of slightly increased memory usage.

Example:

const stream = fs.createReadStream('video.mp4');
console.log(stream.readableHighWaterMark);  // Outputs: 65536 (64 KB)

This change boosts stream performance, particularly in file or network I/O operations, while ensuring the system can handle larger chunks of data more efficiently.

File Pattern Matching with glob and globSync

Node.js 22 introduces built-in glob and globSync functions, making it easier to match file paths based on patterns without requiring external packages like glob.

Example:

import { glob, globSync } from 'node:fs';

glob('*', (err, files) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log(files);
});

const files = globSync('*.js');
console.log(files);

This functionality provides an easy way to search for files or directories matching specific patterns in your file system, helping automate tasks like cleaning up files, running specific tests, or processing batches of files.

Improved AbortSignal Creation Performance

Node.js 22 improves the performance of creating AbortSignal instances. This leads to significant performance improvements when working with features that rely on AbortSignal, such as fetch() and the test runner.

Example:

let controller;
const url = './video.mp4';

const downloadBtn = document.querySelector('.download');
const abortBtn = document.querySelector('.abort');

downloadBtn.addEventListener('click', fetchVideo);

abortBtn.addEventListener('click', () => {
  if (controller) {
    controller.abort();
    console.log('Download aborted');
  }
});

async function fetchVideo() {
  controller = new AbortController();
  const signal = controller.signal;

  try {
    const response = await fetch(url, { signal });
    console.log('Download complete');
  } catch (err) {
    if (err.name === 'AbortError') {
      console.log('Fetch aborted');
    } else {
      console.error('Download error:', err.message);
    }
  }
}

This enhancement reduces overhead when handling signal-based interruptions, such as aborting HTTP requests or long-running tasks.

Deprecations and Removals

As with every Node.js release, some features are deprecated or removed to keep the platform lean and modern. There is more than 185 feature deprecated by following:

You can refer to the https://nodejs.org/api/deprecations.html page for the full list.

When to Upgrade from Node.js 20 to 22 for a Complex Medium-Level Application

For a medium-complexity project, upgrading from Node.js 20 to 22 can offer significant advantages:

  1. ESM Integration: If your project is moving towards ECMAScript Modules (ESM), Node.js 22’s ability to require() ESM modules synchronously can ease the transition from CommonJS, simplifying hybrid module handling.
  2. Performance Enhancements: V8 engine improvements in version 12.4 boost performance for JavaScript operations. For apps handling large datasets or computational tasks, features like Array.fromAsync() help streamline async operations.
  3. Improved Streaming and Buffer Handling: Projects that handle streams, video, or large file processing will benefit from the higher default highWaterMark for streams (increased from 16 KB to 64 KB). This can optimize performance when handling I/O-heavy tasks or large file transfers.
  4. WebSocket Applications: For projects requiring real-time communication, the native WebSocket client is now enabled by default, eliminating the need for third-party WebSocket libraries.
  5. Set Operations and Data Management: If your application involves complex data manipulation, new methods for Set objects like .union(), .intersection(), and .difference() can simplify operations without extra utilities, reducing overhead and improving readability.
  6. Watch Mode: If you rely on tools like nodemon during development, Node.js 22’s stable Watch Mode can make development smoother by automatically restarting processes when code changes, eliminating external dependencies.

When You Might Delay Upgrading:

  • Dependencies: If your project relies on packages that are not yet compatible with Node.js 22, upgrading could introduce issues.
  • Stability: For production environments where stability is critical, ensure all aspects of Node.js 22 (including experimental features) meet your project’s stability requirements before upgrading.

Upgrade Recommendations:

  • Microservices or APIs: Improved stream handling and performance boosts make it ideal for upgrading APIs handling large payloads.
  • Real-Time Applications: WebSocket native support improves real-time communication features, such as chat or notification systems.

Upgrade version Node.js 20 to Node.js 22

To upgrade your existing project from Node.js 20 to Node.js 22, follow these steps:

Step 1: Check the Current Node.js Version

First, verify the current version of Node.js running in your project by using the following command:

node -v

Step 2: Upgrade Node.js to Version 22

Option 1: Using Node Version Manager (NVM)

If you are using NVM (Node Version Manager), upgrading Node.js is simple:

// Install Node.js 22.
nvm install 22
// Switch to Node.js 22.
nvm use 22
// Set Node.js 22 as the default version.
nvm alias default 22
// Verify the installation.
node -v

Option 2: Manual Installation Without NVM

If you’re not using NVM, you need to manually uninstall Node.js 20 and install Node.js 22.

For Linux:

// Uninstall Node.js 20
sudo apt-get remove nodejs
// Install Node.js 22
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs

For macOS:

brew install node@22

For Windows:

  1. Uninstall Node.js 20 from the Control Panel under “Add or Remove Programs.”
  2. Download and install Node.js 22 from the Node.js official website.

Step 3: Update Project Dependencies

After upgrading Node.js, you may need to update your project’s dependencies to ensure compatibility.

  • Update npm (if needed):
npm install -g npm
  • Update your project’s dependencies:
npm update
  • Check for outdated dependencies:
npm outdated

Consider upgrading any outdated packages to ensure compatibility with Node.js 22.

Conclusion

In conclusion, if your project can leverage these new features, upgrading to Node.js 22 can offer tangible benefits in performance, simplicity, and modern JavaScript support. However, always ensure compatibility testing before upgrading production environments.

Node.js 22 introduces a wide range of features and performance enhancements, making it easier for developers to build fast, scalable, and modern applications. From improvements in ESM module loading to new Set methods and enhanced stream performance, this release ensures that Node.js remains one of the most powerful and developer-friendly platforms available.

Picture of Em Ha Tuan

Em Ha Tuan

Leave a Comment

Your email address will not be published. Required fields are marked *

Suggested Article

Scroll to Top