Node.js is an open-source JavaScript runtime environment that allows you to run JavaScript code on the server-side. It is widely used for developing fast, scalable, and efficient web applications, such as APIs, microservices, chatbots, streaming platforms, and more. Node.js has a large and active community of developers who contribute to its improvement and innovation.
In this article, we will explore what’s new and why you should upgrade to Node.js 21, the latest version of Node.js that was released on October 17th, 2023. We will cover some of the main features and changes that Node.js 21.
Table of Contents
Features New Node.js 21
Node.js is a popular and powerful JavaScript runtime environment that allows you to run JavaScript code on the server-side. Node.js is constantly evolving and adding new features and improvements to enhance its performance, compatibility, and usability. We will introduce some of the new Node.js features that were introduced in Node.js 21, the latest version of Node.js.

Stable fetch/Web Streams
Fetch is a modern API that allows you to make HTTP requests from browsers or servers. It is based on promises and provides a simple and consistent way to fetch resources from different sources. WebStreams are a standard API that allows you to process data in small chunks as streams. They are useful for handling large or continuous data flows, such as audio, video, or network requests.
Node.js 21 supports fetch and WebStreams as stable modules, meaning that they are no longer experimental or behind a flag. This means that you can use them in your Node.js applications without any additional configuration or installation. This also means that they are compatible with the browser implementations of these APIs, making it easier to write cross-platform code.
To use fetch and WebStreams in your Node.js applications, you can import them from the node:fetch module:
import {fetch, Request, Response} from 'node:fetch';
You can also import them from the node:stream/web module:
import { ReadableStream, WritableStream } from 'node:stream/web';
Built-in WebSocket client
WebSocket is a protocol that enables bidirectional and real-time communication between clients and servers. It is commonly used for developing interactive and dynamic web applications, such as chat apps, live updates, online games, and more. WebSocket allows you to send and receive messages over a single TCP connection, without the overhead of HTTP requests and responses.
Node.js 21 introduces an experimental WebSocket client implementation that is compatible with the browser WebSocket API. This means that you can connect to a WebSocket server from your Node.js applications, without using any third-party packages or libraries. This also means that you can write WebSocket code that works both in browsers and in Node.js, making it easier to share code between different environments.
To use the WebSocket client in your Node.js applications, you need to enable the –experimental-websocket flag when running Node.js. For example:
node --experimental-websocket app.js
Then, you can import the WebSocket constructor from the node:ws module:
import { WebSocket } from 'node:ws';
You can also import it from the node:global module, which exposes the global objects that are available in browsers:
import { WebSocket } from 'node:global';
V8 11.8
V8 is the JavaScript engine that powers Node.js. It is responsible for compiling and executing JavaScript code, as well as providing various features and optimizations. V8 is also used by Chromium, the open-source project behind Google Chrome and other browsers.
Node.js 21 updates V8 to version 11.8, which is part of Chromium 118. This brings improved performance and new language features to Node.js, such as:
- Array grouping: This feature allows you to group array elements by a key function or value. It returns an object with keys corresponding to the groups and values corresponding to arrays of grouped elements. For example:
const animals = [
{ name: 'Fluffy', type: 'cat' },
{ name: 'Spot', type: 'dog' },
{ name: 'Fido', type: 'dog' },
{ name: 'Snowy', type: 'cat' },
];
// Group animals by type
const groupedByType = animals.groupBy((animal) => animal.type);
// Log the result
console.log(groupedByType);
/*
{
cat: [ { name: 'Fluffy', type: 'cat' }, { name: 'Snowy', type: 'cat' } ],
dog: [ { name: 'Spot', type: 'dog' }, { name: 'Fido', type: 'dog' } ]
}
*/
- ArrayBuffer.prototype.transfer: This feature allows you to transfer the ownership of an ArrayBuffer to a new one with a different size. It returns a new ArrayBuffer with the same contents as the original one, but with a new memory allocation. The original ArrayBuffer becomes detached and unusable. For example:
// Create a new ArrayBuffer with 16 bytes
const buffer = new ArrayBuffer(16);
// Fill it with some data
const view = new
Uint8Array(buffer); for (let i = 0; i < view.length; i++) { view[i] = i; }
// Log the original buffer size and contents console.log(buffer.byteLength); // 16 console.log(view); // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, …]
// Transfer the buffer to a new one with a smaller size const newBuffer = buffer.transfer(8);
// Log the new buffer size and contents console.log(newBuffer.byteLength); // 8 console.log(new Uint8Array(newBuffer)); // [ 0, 1, 2, 3, 4, 5, 6, 7 ]
// Log the original buffer size and contents console.log(buffer.byteLength); // 0 console.log(view); // TypeError: Cannot perform %TypedArray%.prototype.set on a detached ArrayBuffer
– Error cause: This feature allows you to specify the cause of an error when creating a new Error object. It adds a cause property to the error object that contains the original error or a custom message. This can help with debugging and tracing errors in complex applications. For example:
```js
// Create a custom error class
class CustomError extends Error {
constructor(message, cause) {
super(message);
this.name = 'CustomError';
// Set the cause property using the Error cause feature
this.cause = cause;
}
}
// Throw a custom error with a cause
try {
throw new CustomError('Something went wrong', new Error('Invalid input'));
} catch (error) {
// Log the error name, message, and cause
console.log(error.name); // CustomError
console.log(error.message); // Something went wrong
console.log(error.cause); // Error: Invalid input
}
These are just some of the new features and improvements that V8 11.8 brings to Node.js. You can find more details and examples in the.
Support for globs in the Node.js test runner
Globs are patterns that can match multiple files or directories using wildcards and other special characters. They are useful for simplifying file matching expressions and selecting files based on certain criteria.
Node.js 21 adds support for glob expressions in the test runner, which is a tool that allows you to run tests for Node.js core modules. This means that you can use globs to specify which tests you want to run, without having to list them individually or use regular expressions.
To use globs in the test runner, you need to pass the –glob flag followed by a glob pattern. For example:
# Run all tests in the parallel directory
node test/parallel/test*.js
# Run all tests in the parallel directory that start with test-http
node test/parallel/test-http*.js
# Run all tests in the parallel directory that end with .mjs
node --glob='*.mjs' test/parallel
# Run all tests in the parallel directory that contain stream in their name
node --glob='*stream*' test/parallel
# Run all tests in the parallel directory except those that contain crypto in their name
node --glob='!(*crypto*)' test/parallel
You can also combine multiple glob patterns using commas. For example:
# Run all tests in the parallel directory that start with test-http or test-tls
node --glob='test-http*,test-tls*' test/parallel
# Run all tests in the parallel directory except those that end with .mjs or contain crypto in their name
node --glob='!(*.mjs|*crypto*)' test/parallel
You can find more information and examples about globs in the [Node.js documentation].
ESM: –experimental-default-type flag to flip module defaults
ES modules are a standard way of writing modular JavaScript code that supports features such as import and export statements, static analysis, tree shaking, and more. CommonJS modules are a legacy way of writing modular JavaScript code that uses require and module.exports statements, dynamic loading, caching, and more.
Node.js supports both ES modules and CommonJS modules, but they have different behaviors and limitations. For example, ES modules are always strict mode code, while CommonJS modules are not. ES modules have a static module resolution algorithm, while CommonJS modules have a dynamic one. ES modules use file extensions to determine the module type, while CommonJS modules do not.
Node.js 21 provides a new flag to change the default module system that Node.js uses when loading files. By default, Node.js treats files with .js or .cjs extensions as CommonJS modules, and files with .mjs or. Ems extensions as ES modules. However, you can use the –experimental-default-type flag to flip this behavior and treat files with .js or .cjs extensions as ES modules, and files with .mjs or .esm extensions as CommonJS modules.
This flag can be useful for migrating your code from CommonJS to ES modules, or for using ES modules in environments that expect CommonJS modules. However, it is still experimental and may change or be removed in the future. You can find more details and caveats about this flag in the [Node.js documentation].
Frequently Asked Questions
What is the Release Schedule of Node.js?
Node.js follows a release schedule that is based on [Semantic Versioning]. Major versions are released every six months, usually in April and October. Minor and patch versions are released as needed. Every major version has a codename that starts with a letter of the alphabet. For example, Node.js 21 has the codename ‘Fermium’.
How can I Check Which Version of Node.js I am Using?
You can check which version of Node.js 21 you are using by running the node -v command in your terminal. For example:
node -v v21.0.0.
Conclusion
In conclusion, Node.js 21 marks a significant stride forward, introducing a host of compelling features and enhancements. From a revamped V8 JavaScript engine to browser-compatible modules, bidirectional communication with WebSocket, and versatile testing capabilities, this update elevates your development experience and performance.
To embrace these advancements, consider trying out Node.js 21 or upgrading to it, easily accessible on the official website. For more in-depth information and detailed documentation, explore the official website and release notes.