Node.js v20 is now available! On April 18, 2023, it was made public and can now be used by everyone. With this release, Node.js should be much more secure and perform better.

It’s important to know — since this is an even-numbered release, it is slated to become a long-term support (LTS) version in October 2023 and will be supported until April 2026. However, the Node.js team is actively seeking community feedback to identify and fix any issues before it is promoted to LTS.

This new release of Node.js brings exciting updates and features that developers will surely appreciate such as the experimental Permission Model, synchronous import.meta.resolve, a stable test runner, updates the V8 JavaScript engine to version 11.3, bringing performance improvements and bug fixes, and lots more.

In this article, you will explore the changes introduced in Node.js v20, providing an in-depth look at its new features and capabilities.

Getting Started With Node.js v20

Installing Node.js v20 is quite similar to other versions of Node.js. All you need to do is:

  1. Download the installer package from the official Node.js website.
  2. Once you have downloaded the installer, run it and follow the instructions to complete the installation process. This process is pretty straightforward, and you just need to agree to the license agreement and click the “Next” button.
  3. Restart Your System/Machine after the installation process is complete to ensure that all changes take effect.
  4. Verify your Node.js installation by running the following command:
node -v
Verify Node.js v20 Installation
Node.js v20 Installation

If you see the version number (v20.0.0), it means that Node.js is installed correctly, and you are ready to start working with Node.js v20.

What’s New in Node.js v20?

Let’s explore the 5 major updates introduced with this Node.js release. These features include:

Experimental Permission Model

The introduction of the experimental Permission Model in Node.js v20 is a significant addition that gives developers more control over access to specific resources during execution.

This new feature is beneficial when security and resource usage are critical, such as in a production environment.

The Permission Model comes with several abilities, including restricting access to the file system, child_process, worker_threads, and native addons.

Developers can use flags such as --allow-fs-read, --allow-fs-write, and --allow-child-process to specify which resources are accessible. To activate these flags, developers need to utilize the --experimental-permission flag in combination with the required permissions.

Here is an example of how to use the Permission Model to allow read and write access to the entire file system:

$ node --experimental-permission --allow-fs-read=* --allow-fs-write=* index.js

Developers can also use the Permission Model to specify access to specific folders and files. For instance, the following command allows write access to the /tmp/ folder:

$ node --experimental-permission --allow-fs-write=/tmp/ --allow-fs-read=/home/index.js index.js

One significant benefit of the Permission Model is the ability to exercise more granular control over the file system’s access. For instance, developers can specify paths and wildcard patterns to allow access to specific folders or files.

$ node --experimental-permission --allow-fs-read=/home/user/* index.js

The above command grants read access to all folders inside the /home/user/ directory.

The permission property of the process object can also be used to check if a specific permission has been granted at runtime. For example, if you want to check if your Node.js process has read access to a specific folder, /home/user/documents, you can use the following code:

if (process.permission.has('fs.read', '/home/user/documents')) {
  console.log('Read access granted to /home/user/documents');
} else {
  console.log('Read access not granted to /home/user/documents');
}

By using the Permission Model, you have finer-grained control over your Node.js processes’ file system access, which can lead to better security and more efficient resource utilization.

It is crucial to note that the Permission Model is still experimental and may change in future releases of Node.js. It is advisable to stay up-to-date with the Permission Model documentation and exercise caution when using these experimental features.

Stable Test Runner

Node.js v20 includes a stable version of the test_runner module that enables developers to build and run JavaScript test suites quickly and easily without installing additional dependencies.

The stable test runner now includes several building blocks, such as describe, it/test, and hooks, for authoring and structuring test files, along with mocking, watch mode, and the ability to run multiple test files in parallel using the node --test command.

Here’s an example of how to use the test runner:

import { test, mock } from 'node:test';
import assert from 'node:assert';
import fs from 'node:fs';

mock.method(fs, 'readFile', async () => "Hello World");
test('synchronous passing test', async (t) => {
  // This test passes because it does not throw an exception.
  assert.strictEqual(await fs.readFile('a.txt'), "Hello World");
});

The test runner offers configurable and custom test reporters via the --test-reporter flag, experimental test coverage through the --experimental-test-coverage flag and mocking capabilities.

While not intended to replace full-featured test frameworks like Jest or Mocha, the stable test runner offers a simple and quick way to create test suites. It has been enhanced significantly since its introduction in Node.js v19, and thanks to end-user testing and feedback, it has now been marked as stable in Node.js v20.

More information can be found in this merged pull request.

V8 JavaScript Engine Updated to 11.3

The V8 JavaScript engine, which powers Node.js, has been updated to version 11.3 in Node.js v20. This brings improved performance and introduces new language features. Some of the new features this update includes:

  • String.prototype.isWellFormed and toWellFormed: These methods are particularly useful for ensuring proper string format. This help ensure that user string input is in the correct UTF-16 format, reducing errors in the age of emojis.
  • Methods that change Array and TypedArray by copy: This can be useful for creating modified copies of arrays without affecting the original data, which is especially relevant in cases where you need to preserve the original data for comparison or other purposes.
  • Resizable ArrayBuffer and growable SharedArrayBuffer: provides greater flexibility, allowing for more efficient memory allocation.
  • RegExp v flag with set notation and properties of strings: adds functionality for regular expressions.
  • WebAssembly tail call: provides a way to optimize certain types of function calls.

These updates showcase the ongoing commitment of the Node.js development community to improving performance and functionality.

Synchronous import.meta.resolve()

In Node.js v20, import.meta.resolve() has been introduced, which makes it easier to write scripts that are not location sensitive. This function returns synchronously, similar to browser behavior, allowing for more efficient execution.

User loader resolve hooks can still be defined as an async function, but import.meta.resolve() will still return synchronously for the application code even if async resolve hooks are loaded.

The synchronous behavior of import.meta.resolve() allows for more efficient code execution, especially when dealing with large amounts of data. As an author, you can define resolve hooks as either async or sync functions, depending on your preference. The application code will still execute synchronously regardless of whether there are async resolve hooks loaded.

Experimental Single Executable Applications (SEA)

Experimental Single Executable Applications (SEA) is a new feature introduced in Node.js v20 that allows bundling your application with the Node.js binary, enabling end users to distribute and run it as a single executable file.

This has been a long-time request from the community, and the team has been refining the approach over the past year.

In Node.js v20, building a single executable app requires injecting a blob prepared by Node.js from a JSON config rather than injecting the raw JS file.

A blob is a file that contains binary data, in this case, prepared by Node.js, and it is injected into the binary. This change was made to enable embedding multiple co-existing resources into the SEA, which opens up new use cases.

Here’s an example of a sea-config.json file:

{ 
      "main": "myscript.js", 
      "output": "sea-prep.blob" 
}

When executed with the command node --experimental-sea-config sea-config.json, the blob is written to the sea-prep.blob file, which can then be injected into the binary.

The SEA feature allows developers to distribute Node.js applications without requiring users to install Node.js. The functionality was created by Darshan Sen, who won the Outstanding Contribution from a New Arrival award as part of the JavaScriptLandia Awards at OpenJS World.

Microsoft, an OpenJS Foundation member, is investigating this feature as a way to reduce vector attacks and empower the Node.js architecture for a better experience. While the SEA feature is still experimental, it represents an exciting new development for the Node.js community.

Performance

Node.js v20 comes with significant improvements to its runtime, with a renewed focus on performance by the newly formed Node.js performance team. The improvements include optimizations to the core parts of the runtime, such as URL, fetch(), and EventTarget.

One of the notable improvements is the reduction in the cost of initializing EventTarget, which has been cut by half, resulting in faster access to all subsystems using it. Additionally, V8 Fast API calls have been utilized to enhance performance in APIs like URL.canParse() and timers.

Another specific change is the inclusion of the updated version 2.0 of Ada, a fast and spec-compliant URL parser written in C++.

Summary

In this article, you have learned some of the major features (experimental and stable) and improvements that Node.js v20 brings, such as improvements to the V8 JavaScript engine, performance, test runner and introduction of experimental permission model, and single execution applications.

It’s important to know that Node.js v14 will go End-of-Life in April 2023, so it is advised to start planning to upgrade to Node.js v18 (LTS) or vNode.js 20 (soon to be LTS).

Would you like to give the latest version of Node.js a spin? You can do that by deploying your Node.js application on Kinsta and get your first $20 on us. We already support Node.js v20, which means you can test its new features and capabilities starting today.

Now it’s your turn! What features or improvements do you find most interesting in Node.js v20? Are there any significant ones that we may have overlooked? Let us know in the comments.

Joel Olawanle Kinsta

Joel is a Frontend developer working at Kinsta as a Technical Editor. He is a passionate teacher with love for open source and has written over 200 technical articles majorly around JavaScript and it's frameworks.