What's New in Node.js 21
Tags: node
Published: 2023-11-01 12:35:29

[Node.js v21](https://nodejs.org/en/blog/release/v21.0.0), the latest Node update, is now available as a stable release. It supersedes v20 in the ['Current' release line](https://nodejs.org/en/download/current), which has been promoted to the [long-term support](https://nodejs.org/en/download) (LTS) channel as of 24 October.

This new version stabilizes the `fetch` and `WebStreams` API, adds a built-in WebSocket client, improves support for ES modules, and brings some notable performance improvements (as well as a customary update of Node.js dependencies such as V8).

Without further ado, let's jump in and see what Node 21 has in store for us!

## Stable Fetch API Status

![Fetch API is now stable](https://blog.appsignal.com/images/blog/2023-10/fetch-stable.png)

A browser-compatible implementation of the Fetch API was introduced in Node.js in v17.5.0 through an `--experimental-fetch` flag, which brought the `fetch`, `Headers`, `Request`, and `Response` globals. From v18.x to v20.x, the flag was no longer required (but the Fetch API continued to be marked as experimental).

With the release of Node.js 21, the Fetch API is finally promoted to stable status. If you've hesitated to use Fetch due to its experimental nature, you can now confidently adopt it in your applications:

```javascript
const res = await fetch("https://jsonplaceholder.typicode.com/todos/1");
if (res.ok) {
  const data = await res.json();
  console.log(data);
}
```

## Web Streams API Is Stable

![Webstreams API is now stable](https://blog.appsignal.com/images/blog/2023-10/webstreams-stable.png)

The Fetch API implementation is dependent on the `AbortController` interface (for aborting fetch requests) and a browser-compatible [Web Streams API](https://nodejs.org/api/webstreams.html), which were both added in v15.0.0 and v16.5.0, respectively. While `AbortController` quickly became stable in v15.4.0, the Web Streams API has only just been stabilized in v21.

```javascript
import { TextDecoderStream } from "node:stream/web";

async function streamExample() {
  const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
  const stream = response.body;
  const textStream = stream.pipeThrough(new TextDecoderStream());

  for await (const chunk of textStream) {
    console.log(chunk);
  }
}

streamExample();
```

Note that the [older Node.js streams API](https://nodejs.org/api/stream.html) hasn't been deprecated or removed. It will co-exist with the new API, and it even provides a way to convert from a web stream and vice versa using the `.fromWeb()` and `.toWeb()` methods (both added in [Node 17](https://blog.appsignal.com/2021/10/27/what-s-new-in-nodejs-17.html), although both methods still retain their experimental status).

## A Built-in WebSocket Client

Node.js continues its trend of implementing standard web platform APIs with the recent addition of the `--experimental-websocket` flag, introducing a [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
global, as [standardized by WHATWG](https://websockets.spec.whatwg.org/#the-websocket-interface).

This latest effort complements existing web platform APIs such as the aforementioned Fetch and Web Streams APIs, `AbortController` APIs, the [WHATWG URL parser](https://nodejs.org/dist/latest-v21.x/docs/api/url.html#url_the_whatwg_url_api), [EventTarget](https://nodejs.org/api/events.html#nodejs-eventtarget-vs-dom-eventtarget), [Web Crypto API](https://nodejs.org/dist/latest-v21.x/docs/api/webcrypto.html), and more.

```javascript
// index.js
const socket = new WebSocket("ws://localhost:8080");

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

socket.addEventListener("message", (event) => {
  console.log("Message from server ", event.data);
});
```

While it's currently not recommended for production use, you can start experimenting with it right away:

```shell
node --experimental-websocket index.js
```

## ES Modules Improvements

Node.js 21 introduces a new `--experimental-default-type` flag that allows you to modify the default behavior for input files not explicitly defined as ES modules or CommonJS. Prior to this update, such input was implicitly interpreted as CommonJS. With `--experimental-default-type=module`, you can enable a complete ES module implementation in the following scenarios:

- String input provided via `--eval` or `STDIN`, if `--input-type` is unspecified.
- Files ending in `.js` or with no extension, if there is no `package.json` or the nearest parent `package.json` field lacks a type field (except if in `node_modules`).

```shell
node --experimental-default-type=module index.js
```

These improvements pave the way to eventually supporting ES module syntax by default with minimal breaking changes.

## Test Runner Improvements

Building on its stabilization in Node.js v20, the [native test runner](https://nodejs.org/api/test.html) undergoes further enhancements in version 21.

It now supports glob expressions, allowing you to easily match multiple test files using custom glob patterns besides the predefined [defaults](https://nodejs.org/api/test.html#running-tests-from-the-command-line).

```shell
node --test **/*.test.js **/*.spec.js
```

## Module Customization Improvements

This release simplifies module customization by removing the confusing `globalPreload` hook in favor of the [register API](https://nodejs.org/api/module.html#moduleregisterspecifier-parenturl-options) for sending data from the application thread to the customization hooks, and the [initialize hook](https://nodejs.org/api/module.html#initialize) to establish
communication channels between threads.

## llhttp v9.1.2 Strict Mode Enforcement

In the llhttp v9.1.2 update for Node.js, strict mode enforcement was introduced as the default setting, enabling previously included strict mode features for enhanced code reliability and security.

Notable changes include a mandatory `\r\n` presence after headers and chunks, ensuring consistent data processing, and disallowing data transmission after a message with the `Connection: close` header, improving protocol adherence and connection handling.

To maintain backward compatibility with older releases, you can use the `--insecure-http-parser` flag to disable these changes.

## Performance Improvements to Key APIs

[The Node.js performance team](https://github.com/nodejs/performance) landed a few key improvements in this release:

- The Streams API underwent optimizations, achieving approximately a 10% speedup by eliminating redundant checks, utilizing bitmaps, and scheduling callbacks more efficiently.
- When uncorking a response, splitting chunked responses into multiple parts is avoided, which helps reduce unnecessary overhead on both the client and server side.

## V8 Upgraded to v11.8

The V8 engine has been upgraded to v11.8, which brings improved performance and the following JavaScript features:

- [Array grouping](https://github.com/tc39/proposal-array-grouping) to combine like data into groups using `Object.groupBy()` and `Map.groupBy()`.
- A new programmatic API (`ArrayBuffer.prototype.transfer`) for transferring ownership of ArrayBuffers, optimized reallocations, and fixing resizable ArrayBuffers into fixed-length ones.
- [WebAssembly extended-const expressions](https://github.com/WebAssembly/extended-const/blob/main/proposals/extended-const/Overview.md).

## NPM Upgraded to v10.2.0

Node.js v21 includes [npm v10.2.0](https://github.com/npm/cli/releases/tag/v10.2.0), which notably introduces a new `sbom` command that allows you to generate a Software Bill of Materials (SBOM) for the current project. You can read more about [the changes in recent NPM releases](https://github.com/npm/cli/releases) on GitHub.

## Node.js 21 Miscellaneous Updates

Other features and improvements available in this release include the following:

- A partial implementation of the global `navigator` object, allowing access to hardware concurrency information via `navigator.hardwareConcurrency`.
- A `flush` option is added to the `fs.writeFile()` family of functions to guarantee that data is flushed to disk at the end of a successful write operation. This prevents redundant data from being read.

## Should You Use Node.js v21 in Production?

You probably shouldn't use Node.js v21 in production. As an odd-numbered October release, it will only be supported for one year, unlike the April even-numbered releases (16, 18, 20, etc.), which receive long-term updates for up to three years.

## Upgrading to Node.js v21

Should you wish to install or upgrade to Node.js v21, use the relevant link for your operating system and architecture on the [Node.js download page](https://nodejs.org/en/download/current/).

Alternatively, a better way to manage Node.js releases on your machine is to use an environment management tool like [Volta](https://volta.sh/) that can install and switch between multiple versions seamlessly.

After [installing the Volta CLI](https://docs.volta.sh/guide/getting-started), run the command below to install the latest version of Node.js:

```shell
volta install node@latest
success: installed and set node@21.0.0 (with npm@10.2.0) as default
```

## Wrapping Up

Node.js v21 introduces numerous improvements in tooling, language features, standard library additions, and performance optimizations, enhancing its position as a robust JavaScript runtime and aligning it more closely with modern alternatives like [Deno](https://blog.appsignal.com/2022/02/09/an-introduction-to-deno-is-it-better-than-nodejs.html) and [Bun](https://bun.sh/).

You can examine the complete list of bug fixes, new features, and other changes in the [official Node.js 21 release notes](https://nodejs.org/en/blog/release/v21.0.0).

To learn more about the Node.js community and how you can contribute, check out the [project's open issues](https://github.com/nodejs/node/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) and [contribution guidelines](https://github.com/nodejs/node/blob/main/CONTRIBUTING.md)
on GitHub.

Thanks for reading!

**P.S. If you liked this post, [subscribe to our JavaScript Sorcery list](https://blog.appsignal.com/javascript-sorcery) for a monthly deep dive into more magical JavaScript tips and tricks.**

**P.P.S. If you need an APM for your Node.js app, go and [check out the AppSignal APM for Node.js](https://www.appsignal.com/nodejs).**