Command-line API#

Node.js comes with a variety of CLI options. These options expose built-in debugging, multiple ways to execute scripts, and other helpful runtime options.

To view this documentation as a manual page in a terminal, run man node.

Synopsis#

node [options] [V8 options] [<program-entry-point> | -e "script" | -] [--] [arguments]

node inspect [<program-entry-point> | -e "script" | <host>:<port>] …

node --v8-options

Execute without arguments to start the REPL.

For more info about node inspect, see the debugger documentation.

Program entry point#

The program entry point is a specifier-like string. If the string is not an absolute path, it's resolved as a relative path from the current working directory. That entry point string is then resolved as if it's been requested by require() from the current working directory. If no corresponding file is found, an error is thrown.

By default, the resolved path is also loaded as if it's been requested by require(), unless one of the conditions below apply—then it's loaded as if it's been requested by import():

  • The program was started with a command-line flag that forces the entry point to be loaded with ECMAScript module loader, such as --import.
  • The file has an .mjs, .mts or .wasm extension.
  • The file does not have a .cjs extension, and the nearest parent package.json file contains a top-level "type" field with a value of "module".

See module resolution and loading for more details.

Options#

Stability: 2 - Stable

All options, including V8 options, allow words to be separated by both dashes (-) or underscores (_). For example, --pending-deprecation is equivalent to --pending_deprecation.

If an option that takes a single value (such as --max-http-header-size) is passed more than once, then the last passed value is used. Options from the command line take precedence over options passed through the NODE_OPTIONS environment variable.

-#

Alias for stdin. Analogous to the use of - in other command-line utilities, meaning that the script is read from stdin, and the rest of the options are passed to that script.

--#

Indicate the end of node options. Pass the rest of the arguments to the script. If no script filename or eval/print script is supplied prior to this, then the next argument is used as a script filename.

--abort-on-uncaught-exception#

Aborting instead of exiting causes a core file to be generated for post-mortem analysis using a debugger (such as lldb, gdb, and mdb).

If this flag is passed, the behavior can still be set to not abort through process.setUncaughtExceptionCaptureCallback() (and through usage of the node:domain module that uses it).

--allow-addons#

Stability: 1.1 - Active development

When using the Permission Model, the process will not be able to use native addons by default. Attempts to do so will throw an ERR_DLOPEN_DISABLED unless the user explicitly passes the --allow-addons flag when starting Node.js.

Example:

// Attempt to require an native addon
require('nodejs-addon-example');
cjs
$ node --permission --allow-fs-read=* index.js
node:internal/modules/cjs/loader:1319
  return process.dlopen(module, path.toNamespacedPath(filename));
                 ^

Error: Cannot load native addon because loading addons is disabled.
    at Module._extensions..node (node:internal/modules/cjs/loader:1319:18)
    at Module.load (node:internal/modules/cjs/loader:1091:32)
    at Module._load (node:internal/modules/cjs/loader:938:12)
    at Module.require (node:internal/modules/cjs/loader:1115:19)
    at require (node:internal/modules/helpers:130:18)
    at Object.<anonymous> (/home/index.js:1:15)
    at Module._compile (node:internal/modules/cjs/loader:1233:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1287:10)
    at Module.load (node:internal/modules/cjs/loader:1091:32)
    at Module._load (node:internal/modules/cjs/loader:938:12) {
  code: 'ERR_DLOPEN_DISABLED'
}
console

--allow-child-process#

Stability: 1.1 - Active development

When using the Permission Model, the process will not be able to spawn any child process by default. Attempts to do so will throw an ERR_ACCESS_DENIED unless the user explicitly passes the --allow-child-process flag when starting Node.js.

Example:

const childProcess = require('node:child_process');
// Attempt to bypass the permission
childProcess.spawn('node', ['-e', 'require("fs").writeFileSync("/new-file", "example")']);
js
$ node --permission --allow-fs-read=* index.js
node:internal/child_process:388
  const err = this._handle.spawn(options);
                           ^
Error: Access to this API has been restricted
    at ChildProcess.spawn (node:internal/child_process:388:28)
    at node:internal/main/run_main_module:17:47 {
  code: 'ERR_ACCESS_DENIED',
  permission: 'ChildProcess'
}
console

The child_process.fork() API inherits the execution arguments from the parent process. This means that if Node.js is started with the Permission Model enabled and the --allow-child-process flag is set, any child process created using child_process.fork() will automatically receive all relevant Permission Model flags.

This behavior also applies to child_process.spawn(), but in that case, the flags are propagated via the NODE_OPTIONS environment variable rather than directly through the process arguments.

--allow-ffi#

Stability: 1.1 - Active development

When using the Permission Model, the process will not be able to use FFI APIs by default. Attempts to use FFI APIs will throw an ERR_ACCESS_DENIED exception unless the user explicitly passes the --allow-ffi flag when starting Node.js. The node:ffi module is only available in builds with FFI support.

Example:

const { DynamicLibrary, suffix } = require('node:ffi');
const lib = new DynamicLibrary(`./mylib.${suffix}`);
js
$ node --permission index.js
Error: Access to this API has been restricted. Use --allow-ffi to manage permissions.
    at node:internal/main/run_main_module:17:47 {
  code: 'ERR_ACCESS_DENIED',
  permission: 'FFI'
}
console

--allow-fs-read#

This flag configures file system read permissions using the Permission Model.

The valid arguments for the --allow-fs-read flag are:

  • * - To allow all FileSystemRead operations.
  • Multiple paths can be allowed using multiple --allow-fs-read flags. Example --allow-fs-read=/folder1/ --allow-fs-read=/folder2/

Examples can be found in the File System Permissions documentation.

The initializer module and custom --require modules has a implicit read permission.

$ node --permission -r custom-require.js -r custom-require-2.js index.js
console
  • The custom-require.js, custom-require-2.js, and index.js will be by default in the allowed read list.
process.permission.has('fs.read', 'index.js'); // true
process.permission.has('fs.read', 'custom-require.js'); // true
process.permission.has('fs.read', 'custom-require-2.js'); // true
js

--allow-fs-vfs#

Stability: 1.1 - Active development

When using the Permission Model, a virtual file system cannot be mounted by default: vfs.mount() throws ERR_INVALID_STATE unless the user explicitly passes the --allow-fs-vfs flag when starting Node.js.

A mounted VFS serves paths that the file system permissions do not describe, so mounting one is gated on its own flag rather than on --allow-fs-read or --allow-fs-write.

$ node --experimental-vfs --permission --allow-fs-vfs app.js
console

--allow-fs-write#

This flag configures file system write permissions using the Permission Model.

The valid arguments for the --allow-fs-write flag are:

  • * - To allow all FileSystemWrite operations.
  • Multiple paths can be allowed using multiple --allow-fs-write flags. Example --allow-fs-write=/folder1/ --allow-fs-write=/folder2/

Paths delimited by comma (,) are no longer allowed. When passing a single flag with a comma a warning will be displayed.

Examples can be found in the File System Permissions documentation.

--allow-inspector#

Stability: 1.0 - Early development

When using the Permission Model, the process will not be able to connect through inspector protocol.

Attempts to do so will throw an ERR_ACCESS_DENIED unless the user explicitly passes the --allow-inspector flag when starting Node.js.

Example:

const { Session } = require('node:inspector/promises');

const session = new Session();
session.connect();
js
$ node --permission index.js
Error: connect ERR_ACCESS_DENIED Access to this API has been restricted. Use --allow-inspector to manage permissions.
  code: 'ERR_ACCESS_DENIED',
}
console

--allow-net#

Stability: 1.1 - Active development

When using the Permission Model, the process will not be able to access network by default. Attempts to do so will throw an ERR_ACCESS_DENIED unless the user explicitly passes the --allow-net flag when starting Node.js.

Example:

const http = require('node:http');
// Attempt to bypass the permission
const req = http.get('http://example.com', () => {});

req.on('error', (err) => {
  console.log('err', err);
});
js
$ node --permission index.js
Error: connect ERR_ACCESS_DENIED Access to this API has been restricted. Use --allow-net to manage permissions.
  code: 'ERR_ACCESS_DENIED',
}
console

--allow-openssl-store#

Stability: 1.1 - Active development

When using the Permission Model, the process will not be able to use OpenSSL STORE loaders by default, for example to load a private key from a <URL> passed to crypto.createPrivateKey(). Attempts to do so will throw an ERR_ACCESS_DENIED unless the user explicitly passes the --allow-openssl-store flag. This permission can be dropped at runtime via permission.drop().

This flag grants broad authority to configured OpenSSL STORE loaders. A loader may access files, devices, tokens, or the network. Access performed by a loader is not constrained by the fs.read, fs.write, or net permission scopes.

--allow-wasi#

Stability: 1.1 - Active development

When using the Permission Model, the process will not be capable of creating any WASI instances by default. For security reasons, the call will throw an ERR_ACCESS_DENIED unless the user explicitly passes the flag --allow-wasi in the main Node.js process.

Example:

const { WASI } = require('node:wasi');
// Attempt to bypass the permission
new WASI({
  version: 'preview1',
  // Attempt to mount the whole filesystem
  preopens: {
    '/': '/',
  },
});
js
$ node --permission --allow-fs-read=* index.js

Error: Access to this API has been restricted
    at node:internal/main/run_main_module:30:49 {
  code: 'ERR_ACCESS_DENIED',
  permission: 'WASI',
}
console

--allow-worker#

Stability: 1.1 - Active development

When using the Permission Model, the process will not be able to create any worker threads by default. For security reasons, the call will throw an ERR_ACCESS_DENIED unless the user explicitly pass the flag --allow-worker in the main Node.js process.

Example:

const { Worker } = require('node:worker_threads');
// Attempt to bypass the permission
new Worker(__filename);
js
$ node --permission --allow-fs-read=* index.js

Error: Access to this API has been restricted
    at node:internal/main/run_main_module:17:47 {
  code: 'ERR_ACCESS_DENIED',
  permission: 'WorkerThreads'
}
console

--bench#

Stability: 1 - Experimental

Starts the Node.js command-line benchmark runner. At least one explicit file or glob pattern is required:

node --experimental-bench --bench benchmark.mjs
node --experimental-bench --bench 'benchmarks/**/*.js'
console

The --experimental-bench flag is required to use this flag or any other --bench-* option.

Quote glob patterns to prevent expansion by the shell. Matching files are sorted and executed serially. By default, each file runs in a separate child process. Benchmark files declare benchmarks using node:bench; they must not call run() themselves. See the benchmark runner documentation for more details.

This flag cannot be combined with --test, --watch, --watch-path, --check, --eval, or --interactive.

--bench-isolation=mode#

Stability: 1 - Experimental

Configures benchmark file isolation. When mode is 'process', each matching file runs in a separate child process. This is the default. Files are still run serially so their measured work does not overlap.

When mode is 'none', all matching files and benchmarks run serially in the benchmark runner process. This reduces startup overhead but allows module, heap, and process state to carry between files. User writes to stdout or stderr also share destinations with benchmark reporters in this mode.

The supported modes are 'process' and 'none'.

--bench-name-pattern=pattern#

Stability: 1 - Experimental

Only runs benchmarks whose full hierarchical name matches the JavaScript regular expression pattern. Non-matching benchmarks are reported as skipped.

--bench-reporter-destination=destination#

Stability: 1 - Experimental

Specifies the destination for the corresponding benchmark reporter. The value can be stdout, stderr, or a file path. A single reporter defaults to stdout when no destination is specified.

--bench-reporter=reporter#

Stability: 1 - Experimental

Specifies a benchmark reporter. The built-in reporters are spec and json. The json reporter emits newline-delimited JSON. A custom reporter can be specified using a module specifier resolved from the current working directory.

This option can be repeated. When multiple reporters are specified, each must have a corresponding --bench-reporter-destination. The default reporter is spec.

--bench-samples=count#

Stability: 1 - Experimental

Overrides the maximum number of measured callback invocations for every selected benchmark. A benchmark may finish earlier by calling context.done(). count must be an integer between 1 and 4294967295.

--bench-warmup=count#

Stability: 1 - Experimental

Overrides the number of unreported warmup callback invocations for every selected benchmark. count must be an integer between 0 and 4294967295.

--build-sea=config#

Stability: 1.1 - Active development

Generates a single executable application from a JSON configuration file. The argument must be a path to the configuration file. If the path is not absolute, it is resolved relative to the current working directory.

For configuration fields, cross-platform notes, and asset APIs, see the single executable application documentation.

--build-snapshot#

Generates a snapshot blob when the process exits and writes it to disk, which can be loaded later with --snapshot-blob.

When building the snapshot, if --snapshot-blob is not specified, the generated blob will be written, by default, to snapshot.blob in the current working directory. Otherwise it will be written to the path specified by --snapshot-blob.

$ echo "globalThis.foo = 'I am from the snapshot'" > snapshot.js

# Run snapshot.js to initialize the application and snapshot the
# state of it into snapshot.blob.
$ node --snapshot-blob snapshot.blob --build-snapshot snapshot.js

$ echo "console.log(globalThis.foo)" > index.js

# Load the generated snapshot and start the application from index.js.
$ node --snapshot-blob snapshot.blob index.js
I am from the snapshot
console

The v8.startupSnapshot API can be used to specify an entry point at snapshot building time, thus avoiding the need of an additional entry script at deserialization time:

$ echo "require('v8').startupSnapshot.setDeserializeMainFunction(() => console.log('I am from the snapshot'))" > snapshot.js
$ node --snapshot-blob snapshot.blob --build-snapshot snapshot.js
$ node --snapshot-blob snapshot.blob
I am from the snapshot
console

For more information, check out the v8.startupSnapshot API documentation.

The snapshot currently only supports loading a single entrypoint during the snapshot building process, which can load built-in modules, but not additional user-land modules. Users can bundle their applications into a single script with their bundler of choice before building a snapshot.

As it's complicated to ensure the serializablility of all built-in modules, which are also growing over time, only a subset of the built-in modules are well tested to be serializable during the snapshot building process. The Node.js core test suite checks that a few fairly complex applications can be snapshotted. The list of built-in modules being captured by the built-in snapshot of Node.js is considered supported. When the snapshot builder encounters a built-in module that cannot be serialized, it may crash the snapshot building process. In that case a typical workaround would be to delay loading that module until runtime, using either v8.startupSnapshot.setDeserializeMainFunction() or v8.startupSnapshot.addDeserializeCallback(). If serialization for an additional module during the snapshot building process is needed, please file a request in the Node.js issue tracker and link to it in the tracking issue for user-land snapshots.

--build-snapshot-config#

Specifies the path to a JSON configuration file which configures snapshot creation behavior.

The following options are currently supported:

  • builder <string> Required. Provides the name to the script that is executed before building the snapshot, as if --build-snapshot had been passed with builder as the main script name.
  • withoutCodeCache <boolean> Optional. Including the code cache reduces the time spent on compiling functions included in the snapshot at the expense of a bigger snapshot size and potentially breaking portability of the snapshot.

When using this flag, additional script files provided on the command line will not be executed and instead be interpreted as regular command line arguments.

-c, --check#

Syntax check the script without executing.

--completion-bash#

Print source-able bash completion script for Node.js.

node --completion-bash > node_bash_completion
source node_bash_completion
bash

-C condition, --conditions=condition#

Provide custom conditional exports resolution conditions.

Any number of custom string condition names are permitted.

The default Node.js conditions of "node", "default", "import", and "require" will always apply as defined.

For example, to run a module with "development" resolutions:

node -C development app.js
bash

--cpu-prof#

Starts the V8 CPU profiler on start up, and writes the CPU profile to disk before exit.

If --cpu-prof-dir is not specified, the generated profile is placed in the current working directory.

If --cpu-prof-name is not specified, the generated profile is named CPU.${yyyymmdd}.${hhmmss}.${pid}.${tid}.${seq}.cpuprofile.

$ node --cpu-prof index.js
$ ls *.cpuprofile
CPU.20190409.202950.15293.0.0.cpuprofile
console

If --cpu-prof-name is specified, the provided value is used as a template for the file name. The following placeholder is supported and will be substituted at runtime:

  • ${pid} — the current process ID
$ node --cpu-prof --cpu-prof-name 'CPU.${pid}.cpuprofile' index.js
$ ls *.cpuprofile
CPU.15293.cpuprofile
console

--cpu-prof-dir#

Specify the directory where the CPU profiles generated by --cpu-prof will be placed.

The default value is controlled by the --diagnostic-dir command-line option.

--cpu-prof-interval#

Specify the sampling interval in microseconds for the CPU profiles generated by --cpu-prof. The default is 1000 microseconds.

--cpu-prof-name#

Specify the file name of the CPU profile generated by --cpu-prof.

--diagnostic-dir=directory#

Set the directory to which all diagnostic output files are written. Defaults to current working directory.

Affects the default output directory of:

--disable-proto=mode#

Disable the Object.prototype.__proto__ property. If mode is delete, the property is removed entirely. If mode is throw, accesses to the property throw an exception with the code ERR_PROTO_ACCESS.

--disable-sigusr1#

Disable the ability of starting a debugging session by sending a SIGUSR1 signal to the process.

--disable-warning=code-or-type#

Stability: 2 - Stable

Disable specific process warnings by code or type.

Warnings emitted from process.emitWarning() may contain a code and a type. This option will not-emit warnings that have a matching code or type.

List of deprecation warnings.

The Node.js core warning types are: DeprecationWarning and ExperimentalWarning

For example, the following script will not emit DEP0025 require('node:sys') when executed with node --disable-warning=DEP0025:

import sys from 'node:sys';
const sys = require('node:sys');
javascript

For example, the following script will emit the DEP0025 require('node:sys'), but not any Experimental Warnings (such as ExperimentalWarning: vm.measureMemory is an experimental feature in <=v21) when executed with node --disable-warning=ExperimentalWarning:

import sys from 'node:sys';
import vm from 'node:vm';

vm.measureMemory();
const sys = require('node:sys');
const vm = require('node:vm');

vm.measureMemory();
javascript

--disable-wasm-trap-handler#

Node.js enables V8's trap-handler-based WebAssembly bound checks on 64-bit platforms, which significantly improves WebAssembly performance by eliminating the need for inline bound checks. This optimization requires allocating a large virtual memory cage per WebAssembly memory instance (currently typically 8GB for 32-bit WebAssembly memory, 16GB for 64-bit WebAssembly memory) to trap out-of-bound accesses. On most 64-bit platforms, the virtual memory address space is usually large enough (around 128TB) to accommodate typical WebAssembly usages, but if the machine has manual limits on virtual memory (e.g. through ulimit -v), WebAssembly memory allocation is more likely to fail with WebAssembly.Memory(): could not allocate memory.

At startup, Node.js automatically checks whether there is enough virtual memory available to allocate at least one cage, and if not, the trap-handler optimization is automatically disabled so that WebAssembly can still run using inline bound checks (with less optimal performance). But if the application needs to create many WebAssembly memory instances and the machine still configures a relatively high limit on virtual memory, allocation of WebAssembly memory instances may still fail more quickly than expected due to the raised virtual memory usage.

--disable-wasm-trap-handler fully disables this optimization so that WebAssembly memory instances always use inline bound checks instead of reserving large virtual memory cages. This allows more instances to be created when the virtual memory address space available to the Node.js process is limited.

--disallow-code-generation-from-strings#

Make built-in language features like eval and new Function that generate code from strings throw an exception instead. This does not affect the Node.js node:vm module.

--dns-result-order=order#

Set the default value of order in dns.lookup() and dnsPromises.lookup(). The value could be:

  • ipv4first: sets default order to ipv4first.
  • ipv6first: sets default order to ipv6first.
  • verbatim: sets default order to verbatim.

The default is verbatim and dns.setDefaultResultOrder() have higher priority than --dns-result-order.

--enable-fips#

Enable FIPS mode at startup. With OpenSSL 3, a configured provider named fips must be available and initialize successfully. With OpenSSL 1.1.1, Node.js must be built against a FIPS-capable OpenSSL.

--enable-fips-indicator-events#

Publish OpenSSL FIPS indicator results to the 'crypto.fips.indicator' diagnostics channel. This option requires OpenSSL 3.4 or later. It does not enable FIPS mode or change whether an operation is permitted.

--enable-source-maps#

Enable Source Map support for stack traces.

When using a transpiler, such as TypeScript, stack traces thrown by an application reference the transpiled code, not the original source position. --enable-source-maps enables caching of Source Maps and makes a best effort to report stack traces relative to the original source file.

Overriding Error.prepareStackTrace may prevent --enable-source-maps from modifying the stack trace. Call and return the results of the original Error.prepareStackTrace in the overriding function to modify the stack trace with source maps.

const originalPrepareStackTrace = Error.prepareStackTrace;
Error.prepareStackTrace = (error, trace) => {
  // Modify error and trace and format stack trace with
  // original Error.prepareStackTrace.
  return originalPrepareStackTrace(error, trace);
};
js

Note, enabling source maps can introduce latency to your application when Error.stack is accessed. If you access Error.stack frequently in your application, take into account the performance implications of --enable-source-maps.

--entry-url#

Stability: 1 - Experimental

When present, Node.js will interpret the entry point as a URL, rather than a path.

Follows ECMAScript module resolution rules.

Any query parameter or hash in the URL will be accessible via import.meta.url.

node --entry-url 'file:///path/to/file.js?queryparams=work#and-hashes-too'
node --entry-url 'file.ts?query#hash'
node --entry-url 'data:text/javascript,console.log("Hello")'
bash

--env-file-if-exists=file#

Behavior is the same as --env-file, but an error is not thrown if the file does not exist.

--env-file=file#

Loads environment variables from a file relative to the current directory, making them available to applications on process.env. The environment variables which configure Node.js, such as NODE_OPTIONS, are parsed and applied. If the same variable is defined in the environment and in the file, the value from the environment takes precedence.

You can pass multiple --env-file arguments. Subsequent files override pre-existing variables defined in previous files.

An error is thrown if the file does not exist.

node --env-file=.env --env-file=.development.env index.js
bash

The format of the file should be one line per key-value pair of environment variable name and value separated by =:

PORT=3000
text

Any text after a # is treated as a comment:

# This is a comment
PORT=3000 # This is also a comment
text

Values can start and end with the following quotes: `, " or '. They are omitted from the values.

USERNAME="nodejs" # will result in `nodejs` as the value.
text

Multi-line values are supported:

MULTI_LINE="THIS IS
A MULTILINE"
# will result in `THIS IS\nA MULTILINE` as the value.
text

Export keyword before a key is ignored:

export USERNAME="nodejs" # will result in `nodejs` as the value.
text

If you want to load environment variables from a file that may not exist, you can use the --env-file-if-exists flag instead.

-e, --eval "script"#

Evaluate the following argument as JavaScript. The modules which are predefined in the REPL can also be used in script.

If script starts with -, pass it using = (for example, node --print --eval=-42) so it is parsed as the value of --eval.

On Windows, using cmd.exe a single quote will not work correctly because it only recognizes double " for quoting. In Powershell or Git bash, both ' and " are usable.

It is possible to run code containing inline types unless the --no-strip-types flag is provided.

--experimental-addon-modules#

Stability: 1.2 - Release candidate

Enable experimental import support for .node addons.

--experimental-bench#

Stability: 1 - Experimental

Enable the experimental node:bench module and command-line benchmark runner.

--experimental-config-file=path, --experimental-config-file#

Stability: 1.2 - Release candidate

If present, Node.js will look for a configuration file at the specified path. If the path is not specified, Node.js will look for a node.config.json file in the current working directory. To specify a custom path, use the --experimental-config-file=path form. The space-separated --experimental-config-file path form is not supported. The alias --experimental-default-config-file is equivalent to --experimental-config-file without an argument. Node.js will read the configuration file and apply the settings. The configuration file should be a JSON file with the following structure. vX.Y.Z in the $schema must be replaced with the version of Node.js you are using or latest-vX.x for the latest version of that major release line.

{
  "$schema": "https://nodejs.org/dist/vX.Y.Z/docs/node-config-schema.json",
  "nodeOptions": {
    "import": [
      "amaro/strip"
    ],
    "watch-path": "src",
    "watch-preserve-output": true
  },
  "test": {
    "test-isolation": "process"
  },
  "watch": {
    "watch-preserve-output": true
  }
}
json

The configuration file supports namespace-specific options:

  • The nodeOptions field contains CLI flags that are allowed in NODE_OPTIONS.

  • Namespace fields like test, watch, and permission contain configuration specific to that subsystem.

The configuration file can target a specific Node.js major version with nodeVersion:

{
  "nodeVersion": 25,
  "nodeOptions": {
    "watch-path": "src"
  }
}
json

To keep multiple version-specific configurations in the same file, use the configs array. Node.js will use the first entry whose nodeVersion matches the current Node.js major version:

{
  "$schema": "https://nodejs.org/dist/latest-v26.x/docs/node-config-schema.json",
  "configs": [
    {
      "nodeVersion": 25,
      "config": {
        "$schema": "https://nodejs.org/dist/latest-v25.x/docs/node-config-schema.json",
        "nodeOptions": {
          "watch-path": "src"
        }
      }
    }
  ]
}
json

When configs is used, the top level may only contain $schema and configs. Each configs item must define an integer nodeVersion and an object config. A single top-level config does not require nodeVersion, but if present it must match the current Node.js major version.

When a namespace is present in the configuration file, Node.js automatically enables the corresponding flag (e.g., --test, --watch, --permission). This allows you to configure subsystem-specific options without explicitly passing the flag on the command line.

For example:

{
  "test": {
    "test-isolation": "process"
  }
}
json

is equivalent to:

node --test --test-isolation=process
bash

To disable the automatic flag while still using namespace options, you can explicitly set the flag to false within the namespace:

{
  "test": {
    "test": false,
    "test-isolation": "process"
  }
}
json

No-op flags are not supported. Not all V8 flags are currently supported.

It is possible to use the official JSON schema to validate the configuration file, which may vary depending on the Node.js version. Each key in the configuration file corresponds to a flag that can be passed as a command-line argument. The value of the key is the value that would be passed to the flag.

For example, the configuration file above is equivalent to the following command-line arguments:

node --import amaro/strip --watch-path=src --watch-preserve-output --test-isolation=process
bash

The priority in configuration is as follows:

  1. NODE_OPTIONS and command-line options
  2. Dotenv NODE_OPTIONS
  3. Configuration file

Values in the configuration file will not override the values in the environment variables, command-line options, or the NODE_OPTIONS env file parsed by the --env-file flag.

Keys cannot be duplicated within the same or different namespaces.

The configuration parser will throw an error if the configuration file contains unknown keys or keys that cannot be used in a namespace.

Node.js will not sanitize or perform validation on the user-provided configuration, so NEVER use untrusted configuration files.

--experimental-default-config-file#

Stability: 1.0 - Early development

This flag is an alias for --experimental-config-file without an argument. If present, Node.js will look for a node.config.json file in the current working directory and load it as a configuration file.

--experimental-dtls#

Stability: 1 - Experimental

Enable experimental support for the DTLS protocol. See the dtls documentation for details.

--experimental-eventsource#

Enable exposition of EventSource Web API on the global scope.

--experimental-import-meta-resolve#

Enable experimental import.meta.resolve() parent URL support, which allows passing a second parentURL argument for contextual resolution.

Previously gated the entire import.meta.resolve feature.

--experimental-import-text#

Stability: 1.0 - Early development

Enable experimental support for importing modules with with { type: 'text' }.

--experimental-inspector-network-resource#

Stability: 1.1 - Active Development

Enable experimental support for inspector network resources.

--experimental-loader=module#

This flag is discouraged and may be removed in a future version of Node.js. Please use --import with register() instead.

Specify the module containing exported asynchronous module customization hooks. module may be any string accepted as an import specifier.

This feature requires --allow-worker if used with the Permission Model.

--experimental-network-inspection#

Stability: 1 - Experimental

Enable experimental support for the network inspection with Chrome DevTools.

--experimental-package-map=<path>#

Stability: 1 - Experimental

Enable experimental package map resolution. The path argument specifies the location of a JSON configuration file that defines package resolution mappings.

node --experimental-package-map=./package-map.json app.js
bash

When enabled, bare specifier resolution consults the package map for resolution. This allows explicit control over which packages can import which dependencies.

See Package maps for details on the configuration file format and resolution algorithm.

--experimental-print-required-tla#

If the ES module graph cannot be require()'d because it contains any top-level await, this flag allows Node.js to locate and print their locations.

--experimental-quic#

Stability: 1.1 - Active development

Enable experimental support for the QUIC protocol.

--experimental-sea-config#

Stability: 1 - Experimental

Use this flag to generate a blob that can be injected into the Node.js binary to produce a single executable application. See the documentation about this configuration for details.

--experimental-shadow-realm#

Use this flag to enable ShadowRealm support.

--experimental-storage-inspection#

Stability: 1.1 - Active Development

Enable experimental support for storage inspection

--experimental-stream-iter#

Stability: 1 - Experimental

Enable the experimental node:stream/iter module.

--experimental-test-coverage#

When used in conjunction with the node:test module, a code coverage report is generated as part of the test runner output. If no tests are run, a coverage report is not generated. See the documentation on collecting code coverage from tests for more details.

--experimental-test-module-mocks#

Stability: 1.0 - Early development

Enable module mocking in the test runner.

This feature requires --allow-worker if used with the Permission Model.

--experimental-test-tag-filter='<expr>'#

Stability: 1.0 - Early development

Run only tests that match the provided boolean tag-filter expression. Tests declare tags via the tags option on test(), it(), suite(), or describe(). Tags inherit from suites to nested tests by union.

The expression supports boolean operators (and/&&, or/||, not/!), parentheses for grouping, and * wildcards inside identifiers. Standard precedence applies: not binds tighter than and, which binds tighter than or. See Test tags for the full grammar and behavior.

The flag may be specified more than once; multiple expressions are combined with AND, so a test must satisfy every expression to run.

A malformed expression causes the test runner to exit with a non-zero status before running any tests.

--experimental-vfs#

Stability: 1 - Experimental

Enable the experimental node:vfs module.

--experimental-vm-modules#

Enable experimental ES Module support in the node:vm module.

--experimental-wasi-unstable-preview1#

Enable experimental WebAssembly System Interface (WASI) support.

--experimental-web-worker#

Enable experimental support for the Web Worker API.

--experimental-worker-inspection#

Stability: 1.1 - Active Development

Enable experimental support for the worker inspection with Chrome DevTools.

--force-context-aware#

Disable loading native addons that are not context-aware.

--force-fips[=mode]#

Enable FIPS mode at startup and prevent it from being disabled from script code. The same OpenSSL requirements as --enable-fips apply.

An optional mode can be specified using --force-fips=mode:

  • provider: Preserve the OpenSSL FIPS provider's configured handling of non-approved operations. This is the current default when the mode is omitted.
  • strict: Reject non-approved operations reported through the OpenSSL FIPS indicator callback. This mode requires OpenSSL 3.4 or later.

The strict mode only covers operations reported through the callback for OpenSSL's default library context. It does not cover native addons that use another OSSL_LIB_CTX or another copy of libcrypto, nor operation-specific indicators that do not invoke the callback.

--force-node-api-uncaught-exceptions-policy#

Enforces uncaughtException event on Node-API asynchronous callbacks.

To prevent from an existing add-on from crashing the process, this flag is not enabled by default. In the future, this flag will be enabled by default to enforce the correct behavior.

--frozen-intrinsics#

Stability: 1 - Experimental

Enable experimental frozen intrinsics like Array and Object.

Only the root context is supported. There is no guarantee that globalThis.Array is indeed the default intrinsic reference. Code may break under this flag.

To allow polyfills to be added, --require and --import both run before freezing intrinsics.

--heap-prof#

Starts the V8 heap profiler on start up, and writes the heap profile to disk before exit.

If --heap-prof-dir is not specified, the generated profile is placed in the current working directory.

If --heap-prof-name is not specified, the generated profile is named Heap.${yyyymmdd}.${hhmmss}.${pid}.${tid}.${seq}.heapprofile.

$ node --heap-prof index.js
$ ls *.heapprofile
Heap.20190409.202950.15293.0.001.heapprofile
console

--heap-prof-dir#

Specify the directory where the heap profiles generated by --heap-prof will be placed.

The default value is controlled by the --diagnostic-dir command-line option.

--heap-prof-interval#

Specify the average sampling interval in bytes for the heap profiles generated by --heap-prof. The default is 512 * 1024 bytes.

--heap-prof-name#

Specify the file name of the heap profile generated by --heap-prof.

--heapsnapshot-near-heap-limit=max_count#

Writes a V8 heap snapshot to disk when the V8 heap usage is approaching the heap limit. count should be a non-negative integer (in which case Node.js will write no more than max_count snapshots to disk).

When generating snapshots, garbage collection may be triggered and bring the heap usage down. Therefore multiple snapshots may be written to disk before the Node.js instance finally runs out of memory. These heap snapshots can be compared to determine what objects are being allocated during the time consecutive snapshots are taken. It's not guaranteed that Node.js will write exactly max_count snapshots to disk, but it will try its best to generate at least one and up to max_count snapshots before the Node.js instance runs out of memory when max_count is greater than 0.

Generating V8 snapshots takes time and memory (both memory managed by the V8 heap and native memory outside the V8 heap). The bigger the heap is, the more resources it needs. Node.js will adjust the V8 heap to accommodate the additional V8 heap memory overhead, and try its best to avoid using up all the memory available to the process. When the process uses more memory than the system deems appropriate, the process may be terminated abruptly by the system, depending on the system configuration.

$ node --max-old-space-size=100 --heapsnapshot-near-heap-limit=3 index.js
Wrote snapshot to Heap.20200430.100036.49580.0.001.heapsnapshot
Wrote snapshot to Heap.20200430.100037.49580.0.002.heapsnapshot
Wrote snapshot to Heap.20200430.100038.49580.0.003.heapsnapshot

<--- Last few GCs --->

[49580:0x110000000]     4826 ms: Mark-sweep 130.6 (147.8) -> 130.5 (147.8) MB, 27.4 / 0.0 ms  (average mu = 0.126, current mu = 0.034) allocation failure scavenge might not succeed
[49580:0x110000000]     4845 ms: Mark-sweep 130.6 (147.8) -> 130.6 (147.8) MB, 18.8 / 0.0 ms  (average mu = 0.088, current mu = 0.031) allocation failure scavenge might not succeed


<--- JS stacktrace --->

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
....
console

--heapsnapshot-signal=signal#

Enables a signal handler that causes the Node.js process to write a heap dump when the specified signal is received. signal must be a valid signal name. Disabled by default.

$ node --heapsnapshot-signal=SIGUSR2 index.js &
$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
node         1  5.5  6.1 787252 247004 ?       Ssl  16:43   0:02 node --heapsnapshot-signal=SIGUSR2 index.js
$ kill -USR2 1
$ ls
Heap.20190718.133405.15554.0.001.heapsnapshot
console

-h, --help#

Print node command-line options. The output of this option is less detailed than this document.

--icu-data-dir=file#

Specify ICU data load path. (Overrides NODE_ICU_DATA.)

--import=module#

Stability: 1 - Experimental

Preload the specified module at startup. If the flag is provided several times, each module will be executed sequentially in the order they appear, starting with the ones provided in NODE_OPTIONS.

Follows ECMAScript module resolution rules. Use --require to load a CommonJS module. Modules preloaded with --require will run before modules preloaded with --import.

Modules are preloaded into the main thread as well as any worker threads, forked processes, or clustered processes.

--input-type=type#

This configures Node.js to interpret --eval or STDIN input as CommonJS or as an ES module. Valid values are "commonjs", "module", "module-typescript" and "commonjs-typescript". The "-typescript" values are not available with the flag --no-strip-types. The default is no value, or "commonjs" if --no-experimental-detect-module is passed.

If --input-type is not provided, Node.js will try to detect the syntax with the following steps:

  1. Run the input as CommonJS.
  2. If step 1 fails, run the input as an ES module.
  3. If step 2 fails with a SyntaxError, strip the types.
  4. If step 3 fails with an error code ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX or ERR_INVALID_TYPESCRIPT_SYNTAX, throw the error from step 2, including the TypeScript error in the message, else run as CommonJS.
  5. If step 4 fails, run the input as an ES module.

To avoid the delay of multiple syntax detection passes, the --input-type=type flag can be used to specify how the --eval input should be interpreted.

The REPL does not support this option. Usage of --input-type=module with --print will throw an error, as --print does not support ES module syntax.

--insecure-http-parser#

Enable leniency flags on the HTTP parser. This may allow interoperability with non-conformant HTTP implementations.

When enabled, the parser will accept the following:

  • Invalid HTTP headers values.
  • Invalid HTTP versions.
  • Allow message containing both Transfer-Encoding and Content-Length headers.
  • Allow extra data after message when Connection: close is present.
  • Allow extra transfer encodings after chunked has been provided.
  • Allow \n to be used as token separator instead of \r\n.
  • Allow \r\n not to be provided after a chunk.
  • Allow spaces to be present after a chunk size and before \r\n.

All the above will expose your application to request smuggling or poisoning attack. Avoid using this option.

--inspect-brk[=[host:]port]#

Activate inspector on host:port and break at start of user script. Default host:port is 127.0.0.1:9229. If port 0 is specified, a random available port will be used.

See V8 Inspector integration for Node.js for further explanation on Node.js debugger.

See the security warning below regarding the host parameter usage.

--inspect-port=[host:]port#

Set the host:port to be used when the inspector is activated. Useful when activating the inspector by sending the SIGUSR1 signal. Except when --disable-sigusr1 is passed.

Default host is 127.0.0.1. If port 0 is specified, a random available port will be used.

See the security warning below regarding the host parameter usage.

--inspect-publish-uid=stderr,http#

Specify ways of the inspector web socket url exposure.

By default inspector websocket url is available in stderr and under /json/list endpoint on http://host:port/json/list.

--inspect-wait[=[host:]port]#

Activate inspector on host:port and wait for debugger to be attached. Default host:port is 127.0.0.1:9229. If port 0 is specified, a random available port will be used.

See V8 Inspector integration for Node.js for further explanation on Node.js debugger.

See the security warning below regarding the host parameter usage.

--inspect[=[host:]port]#

Activate inspector on host:port. Default is 127.0.0.1:9229. If port 0 is specified, a random available port will be used.

V8 inspector integration allows tools such as Chrome DevTools and IDEs to debug and profile Node.js instances. The tools attach to Node.js instances via a tcp port and communicate using the Chrome DevTools Protocol. See V8 Inspector integration for Node.js for further explanation on Node.js debugger.

Warning: binding inspector to a public IP:port combination is insecure#

Binding the inspector to a public IP (including 0.0.0.0) with an open port is insecure, as it allows external hosts to connect to the inspector and perform a remote code execution attack.

If specifying a host, make sure that either:

  • The host is not accessible from public networks.
  • A firewall disallows unwanted connections on the port.

More specifically, --inspect=0.0.0.0 is insecure if the port (9229 by default) is not firewall-protected.

See the debugging security implications section for more information.

-i, --interactive#

Opens the REPL even if stdin does not appear to be a terminal.

--jitless#

Stability: 1 - Experimental. This flag is inherited from V8 and is subject to change upstream.

Disable runtime allocation of executable memory. This may be required on some platforms for security reasons. It can also reduce attack surface on other platforms, but the performance impact may be severe.

--localstorage-file=file#

Stability: 1.2 - Release candidate.

The file used to store localStorage data. If the file does not exist, it is created the first time localStorage is accessed. The same file may be shared between multiple Node.js processes concurrently.

--max-http-header-size=size#

Specify the maximum size, in bytes, of HTTP headers. Defaults to 16 KiB.

--max-old-space-size-percentage=percentage#

Sets the maximum memory size of V8's old memory section as a percentage of available system memory. This flag takes precedence over --max-old-space-size when both are specified.

The percentage parameter must be a number greater than 0 and up to 100, representing the percentage of available system memory to allocate to the V8 heap.

Note: This flag utilizes --max-old-space-size, which may be unreliable on 32-bit platforms due to integer overflow issues.

# Using 50% of available system memory
node --max-old-space-size-percentage=50 index.js

# Using 75% of available system memory
node --max-old-space-size-percentage=75 index.js
bash

--network-family-autoselection-attempt-timeout#

Sets the default value for the network family autoselection attempt timeout. For more information, see net.getDefaultAutoSelectFamilyAttemptTimeout().

--no-addons#

Disable the node-addons exports condition as well as disable loading native addons. When --no-addons is specified, calling process.dlopen or requiring a native C++ addon will fail and throw an exception.

--no-async-context-frame#

Disables the use of AsyncLocalStorage backed by AsyncContextFrame and uses the prior implementation which relied on async_hooks. The previous model is retained for compatibility with Electron and for cases where the context flow may differ. However, if a difference in flow is found please report it.

--no-deprecation#

Silence deprecation warnings.

--no-experimental-detect-module#

Disable using syntax detection to determine module type.

--no-experimental-ffi#

Stability: 1 - Experimental

Disable the experimental node:ffi module.

This flag is only available in builds with FFI support.

--no-experimental-global-navigator#

Stability: 1 - Experimental

Disable exposition of Navigator API on the global scope.

--no-experimental-repl-await#

Use this flag to disable top-level await in REPL.

--no-experimental-require-module#

Stability: 3 - Legacy: Use --no-require-module instead.

Legacy alias for --no-require-module.

--no-experimental-sqlite#

Disable the experimental node:sqlite module.

--no-experimental-websocket#

Disable exposition of <WebSocket> on the global scope.

--no-experimental-webstorage#

Stability: 1.2 - Release candidate.

Disable Web Storage support.

--no-extra-info-on-fatal-exception#

Hide extra information on fatal exception that causes exit.

--no-force-async-hooks-checks#

Disables runtime checks for async_hooks. These will still be enabled dynamically when async_hooks is enabled.

--no-global-search-paths#

Do not search modules from global paths like $HOME/.node_modules and $NODE_PATH.

--no-network-family-autoselection#

Disables the family autoselection algorithm unless connection options explicitly enables it.

--no-require-module#

Disable support for loading a synchronous ES module graph in require().

See Loading ECMAScript modules using require().

--no-strip-types#

Disable type-stripping for TypeScript files. For more information, see the TypeScript type-stripping documentation.

--no-warnings#

Silence all process warnings (including deprecations).

--no-worker-snapshot#

Stability: 1 - Experimental

Start worker threads by running the internal bootstrap from scratch instead of deserializing the bootstrapped context from the built-in startup snapshot.

--node-memory-debug#

Enable extra debug checks for memory leaks in Node.js internals. This is usually only useful for developers debugging Node.js itself.

--openssl-config=file#

Load an OpenSSL configuration file on startup. The file can activate an OpenSSL 3 FIPS provider or configure a FIPS-capable OpenSSL 1.1.1 build. See FIPS mode.

This option takes precedence over the OPENSSL_CONF environment variable.

--openssl-legacy-provider#

Enable OpenSSL 3.0 legacy provider. For more information please see OSSL_PROVIDER-legacy.

--openssl-shared-config#

Enable OpenSSL default configuration section, openssl_conf to be read from the OpenSSL configuration file. The default configuration file is named openssl.cnf but this can be changed using the environment variable OPENSSL_CONF, or by using the command line option --openssl-config. The location of the default OpenSSL configuration file depends on how OpenSSL is being linked to Node.js. Sharing the OpenSSL configuration may have unwanted implications and it is recommended to use a configuration section specific to Node.js which is nodejs_conf and is default when this option is not used.

--pending-deprecation#

Emit pending deprecation warnings.

Pending deprecations are generally identical to a runtime deprecation with the notable exception that they are turned off by default and will not be emitted unless either the --pending-deprecation command-line flag, or the NODE_PENDING_DEPRECATION=1 environment variable, is set. Pending deprecations are used to provide a kind of selective "early warning" mechanism that developers may leverage to detect deprecated API usage.

--permission#

Enable the Permission Model for current process. When enabled, the following permissions are restricted:

See also --permission-audit for an audit-only mode that logs violations without denying access.

--permission-audit#

Enable audit mode for the permission model. When enabled, permission checks are performed but access is not denied — no ERR_ACCESS_DENIED error is thrown. Instead, each permission violation is published through the node:diagnostics_channel module, and execution continues normally.

This flag does not require --permission to be specified. The --allow-* flags are not needed in audit mode, since no access is denied.

Audit mode is useful for discovering what permissions your application requires before deploying with --permission. See the Permission Model documentation for the list of diagnostics channel names and the message format.

If both --permission and --permission-audit are specified, --permission takes precedence and the Permission Model runs in enforce mode.

--preserve-symlinks#

Instructs the module loader to preserve symbolic links when resolving and caching modules.

By default, when Node.js loads a module from a path that is symbolically linked to a different on-disk location, Node.js will dereference the link and use the actual on-disk "real path" of the module as both an identifier and as a root path to locate other dependency modules. In most cases, this default behavior is acceptable. However, when using symbolically linked peer dependencies, as illustrated in the example below, the default behavior causes an exception to be thrown if moduleA attempts to require moduleB as a peer dependency:

{appDir}
 ├── app
 │   ├── index.js
 │   └── node_modules
 │       ├── moduleA -> {appDir}/moduleA
 │       └── moduleB
 │           ├── index.js
 │           └── package.json
 └── moduleA
     ├── index.js
     └── package.json
text

The --preserve-symlinks command-line flag instructs Node.js to use the symlink path for modules as opposed to the real path, allowing symbolically linked peer dependencies to be found.

Note, however, that using --preserve-symlinks can have other side effects. Specifically, symbolically linked native modules can fail to load if those are linked from more than one location in the dependency tree (Node.js would see those as two separate modules and would attempt to load the module multiple times, causing an exception to be thrown).

The --preserve-symlinks flag does not apply to the main module, which allows node --preserve-symlinks node_module/.bin/<foo> to work. To apply the same behavior for the main module, also use --preserve-symlinks-main.

--preserve-symlinks-main#

Instructs the module loader to preserve symbolic links when resolving and caching the main module (require.main).

This flag exists so that the main module can be opted-in to the same behavior that --preserve-symlinks gives to all other imports; they are separate flags, however, for backward compatibility with older Node.js versions.

--preserve-symlinks-main does not imply --preserve-symlinks; use --preserve-symlinks-main in addition to --preserve-symlinks when it is not desirable to follow symlinks before resolving relative paths.

See --preserve-symlinks for more information.

-p, --print "script"#

Identical to -e but prints the result.

--process-timeout=duration#

Stability: 1.1 - Active development

Exits the process with code 124 if it is still running after duration, measured from the start of the process. duration is a positive integer followed by a unit: ms, s, m, or h, for example 500ms, 30s, 5m, or 1h. The exit code matches the one used by the timeout(1) command.

Before exiting, Node.js prints what the main thread was doing and which resources were keeping the event loop alive to stderr:

$ node --process-timeout=5s server.js
(node:25418) Process timed out after 5s (--process-timeout). Exiting with code 124.
Main thread was not executing JavaScript.
Resources keeping the event loop alive:
    TCPServerWrap (listening on [::]:3000, fd 20)
    Timeout x2 (next due in 2931ms)
console

If the main thread was executing JavaScript, its stack trace is printed instead. Use --report-on-process-timeout to also generate a diagnostic report.

The process exits without emitting the 'beforeExit' and 'exit' events, as the JavaScript code may be what keeps the process running. Code coverage and profiles, such as those enabled with NODE_V8_COVERAGE=dir or --cpu-prof, are still written.

If the main thread does not respond within two seconds, for example because it is blocked in a synchronous operation such as child_process.execSync(), Node.js exits immediately without printing the stack trace and resources.

This option is not allowed in NODE_OPTIONS, and it cannot be combined with the options that enable the inspector, such as --inspect, --inspect-brk, --inspect-wait, --inspect-port, and --inspect-publish-uid, nor with node inspect, --run, or --build-snapshot. While it is in effect, inspector.open() and session.connectToMainThread() throw, and requests to activate the inspector with SIGUSR1 are ignored.

Child processes that inherit process.execArgv, such as those created with child_process.fork(), apply the timeout from their own start. With --watch, the timeout applies to each run of the application rather than to the process that watches for changes. When running tests from the command line, the test runner process applies the timeout to the whole run, and each test file that runs in its own process applies it as well.

--prof#

Generate V8 profiler output.

--prof-process#

Process V8 profiler output generated using the V8 option --prof.

--redirect-warnings=file#

Write process warnings to the given file instead of printing to stderr. The file will be created if it does not exist, and will be appended to if it does. If an error occurs while attempting to write the warning to the file, the warning will be written to stderr instead.

The file name may be an absolute path. If it is not, the default directory it will be written to is controlled by the --diagnostic-dir command-line option.

--report-compact#

Write reports in a compact format, single-line JSON, more easily consumable by log processing systems than the default multi-line format designed for human consumption.

--report-dir=directory, --report-directory=directory#

Location at which the report will be generated.

--report-exclude-env#

When --report-exclude-env is passed the diagnostic report generated will not contain the environmentVariables data.

--report-exclude-network#

Exclude header.networkInterfaces from the diagnostic report. By default this is not set and the network interfaces are included.

--report-filename=filename#

Name of the file to which the report will be written.

If the filename is set to 'stdout' or 'stderr', the report is written to the stdout or stderr of the process respectively.

--report-on-fatalerror#

Enables the report to be triggered on fatal errors (internal errors within the Node.js runtime such as out of memory) that lead to termination of the application. Useful to inspect various diagnostic data elements such as heap, stack, event loop state, resource consumption etc. to reason about the fatal error.

--report-on-process-timeout#

Stability: 1.1 - Active development

Enables the report to be generated when --process-timeout expires, in addition to the summary printed to stderr. Useful to inspect the JavaScript and native stacks, the event loop state, and resource consumption to reason about why the process did not exit. Requires --process-timeout.

Worker threads that do not provide their part of the report within two seconds, for example because they are blocked in a synchronous operation such as child_process.execSync(), are left out of it.

--report-on-signal#

Enables report to be generated upon receiving the specified (or predefined) signal to the running Node.js process. The signal to trigger the report is specified through --report-signal.

--report-signal=signal#

Sets or resets the signal for report generation (not supported on Windows). Default signal is SIGUSR2.

--report-uncaught-exception#

Enables report to be generated when the process exits due to an uncaught exception. Useful when inspecting the JavaScript stack in conjunction with native stack and other runtime environment data.

-r, --require module#

Preload the specified module at startup.

Follows require()'s module resolution rules. module may be either a path to a file, or a node module name.

Modules preloaded with --require will run before modules preloaded with --import.

Modules are preloaded into the main thread as well as any worker threads, forked processes, or clustered processes.

--run#

This runs a specified command from a package.json's "scripts" object. If a missing "command" is provided, it will list the available scripts.

Passing --run without a command lists the available scripts and exits with a non-zero exit code:

$ node --run
Available scripts are:
  test: node --test
console

--run will traverse up to the root directory and finds a package.json file to run the command from.

--run prepends ./node_modules/.bin for each ancestor of the current directory, to the PATH in order to execute the binaries from different folders where multiple node_modules directories are present, if ancestor-folder/node_modules/.bin is a directory.

--run executes the command in the directory containing the related package.json.

For example, the following command will run the test script of the package.json in the current folder:

$ node --run test
console

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

$ node --run test -- --verbose
console
Intentional limitations#

node --run is not meant to match the behaviors of npm run or of the run commands of other package managers. The Node.js implementation is intentionally more limited, in order to focus on top performance for the most common use cases. Some features of other run implementations that are intentionally excluded are:

  • Running pre or post scripts in addition to the specified script.
  • Defining package manager-specific environment variables.
Environment variables#

The following environment variables are set when running a script with --run:

  • NODE_RUN_SCRIPT_NAME: The name of the script being run. For example, if --run is used to run test, the value of this variable will be test.
  • NODE_RUN_PACKAGE_JSON_PATH: The path to the package.json that is being processed.

Environment variables loaded from a file with --env-file are not applied to the command executed by --run.

--secure-heap-min=n#

When using --secure-heap, the --secure-heap-min flag specifies the minimum allocation from the secure heap. The minimum value is 2. The maximum value is the lesser of --secure-heap or 2147483647. The value given must be a power of two.

--secure-heap=n#

Initializes an OpenSSL secure heap of n bytes. When initialized, the secure heap is used for selected types of allocations within OpenSSL during key generation and other operations. This is useful, for instance, to prevent sensitive information from leaking due to pointer overruns or underruns.

The secure heap is a fixed size and cannot be resized at runtime so, if used, it is important to select a large enough heap to cover all application uses.

The heap size given must be a power of two. Any value less than 2 will disable the secure heap.

The secure heap is disabled by default.

The secure heap is not available on Windows.

See CRYPTO_secure_malloc_init for more details.

--snapshot-blob=path#

Stability: 1 - Experimental

When used with --build-snapshot, --snapshot-blob specifies the path where the generated snapshot blob is written to. If not specified, the generated blob is written to snapshot.blob in the current working directory.

When used without --build-snapshot, --snapshot-blob specifies the path to the blob that is used to restore the application state.

When loading a snapshot, Node.js checks that:

  1. The version, architecture, and platform of the running Node.js binary are exactly the same as that of the binary that generates the snapshot.
  2. The V8 flags and CPU features are compatible with that of the binary that generates the snapshot.

If they don't match, Node.js refuses to load the snapshot and exits with status code 1.

--test#

Starts the Node.js command line test runner. This flag cannot be combined with --watch-path, --check, --eval, --interactive, or the inspector. See the documentation on running tests from the command line for more details.

--test-concurrency#

The maximum number of test files that the test runner CLI will execute concurrently. If --test-isolation is set to 'none', this flag is ignored and concurrency is one. Otherwise, concurrency defaults to os.availableParallelism() - 1.

--test-coverage-branches=threshold#

Stability: 1 - Experimental

Require a minimum percent of covered branches. If code coverage does not reach the threshold specified, the process will exit with code 1.

--test-coverage-exclude#

Stability: 1 - Experimental

Excludes specific files from code coverage using a glob pattern, which can match both absolute and relative file paths.

This option may be specified multiple times to exclude multiple glob patterns.

If both --test-coverage-exclude and --test-coverage-include are provided, files must meet both criteria to be included in the coverage report.

By default all the matching test files are excluded from the coverage report. Specifying this option will override the default behavior.

--test-coverage-functions=threshold#

Stability: 1 - Experimental

Require a minimum percent of covered functions. If code coverage does not reach the threshold specified, the process will exit with code 1.

--test-coverage-include#

Stability: 1 - Experimental

Includes specific files in code coverage using a glob pattern, which can match both absolute and relative file paths.

This option may be specified multiple times to include multiple glob patterns.

If both --test-coverage-exclude and --test-coverage-include are provided, files must meet both criteria to be included in the coverage report.

--test-coverage-include-all#

Stability: 1 - Experimental

Includes source files that were never loaded by the test run in the coverage report, where they are reported as having zero coverage.

Candidate files are searched for in the current working directory, and are subject to the same --test-coverage-include and --test-coverage-exclude filtering as the rest of the report.

--test-coverage-lines=threshold#

Stability: 1 - Experimental

Require a minimum percent of covered lines. If code coverage does not reach the threshold specified, the process will exit with code 1.

--test-force-exit#

Configures the test runner to exit the process once all known tests have finished executing even if the event loop would otherwise remain active.

--test-global-setup=module#

Stability: 1.0 - Early development

Specify a module that will be evaluated before all tests are executed and can be used to setup global state or fixtures for tests.

See the documentation on global setup and teardown for more details.

--test-isolation=mode#

Configures the type of test isolation used in the test runner. When mode is 'process', each test file is run in a separate child process. When mode is 'none', all test files run in the same process as the test runner. The default isolation mode is 'process'. This flag is ignored if the --test flag is not present. See the test runner execution model section for more information.

--test-name-pattern#

A regular expression that configures the test runner to only execute tests whose name matches the provided pattern. See the documentation on filtering tests by name for more details.

If both --test-name-pattern and --test-skip-pattern are supplied, tests must satisfy both requirements in order to be executed.

--test-only#

Configures the test runner to only execute top level tests that have the only option set. This flag is not necessary when test isolation is disabled.

--test-random-seed#

Set the seed used to randomize test execution order. This applies to both test file execution order and queued tests within each file. Providing this flag enables randomization implicitly, even without --test-randomize.

The value must be an integer between 0 and 4294967295.

This flag cannot be used with --watch or --test-rerun-failures.

--test-randomize#

Randomize test execution order. This applies to both test file execution order and queued tests within each file. This can help detect tests that rely on shared state or execution order.

The seed used for randomization is printed in the test summary and can be reused with --test-random-seed.

For detailed behavior and examples, see randomizing tests execution order.

This flag cannot be used with --watch or --test-rerun-failures.

--test-reporter#

A test reporter to use when running tests. See the documentation on test reporters for more details.

--test-reporter-destination#

The destination for the corresponding test reporter. See the documentation on test reporters for more details.

--test-rerun-failures#

A path to a file allowing the test runner to persist the state of the test suite between runs. The test runner will use this file to determine which tests have already succeeded or failed, allowing for re-running of failed tests without having to re-run the entire test suite. The test runner will create this file if it does not exist. See the documentation on test reruns for more details.

--test-shard#

Test suite shard to execute in a format of <index>/<total>, where

  • index is a positive integer, index of divided parts.
  • total is a positive integer, total of divided part.

This command will divide all tests files into total equal parts, and will run only those that happen to be in an index part.

For example, to split your tests suite into three parts, use this:

node --test --test-shard=1/3
node --test --test-shard=2/3
node --test --test-shard=3/3
bash

--test-skip-pattern#

A regular expression that configures the test runner to skip tests whose name matches the provided pattern. See the documentation on filtering tests by name for more details.

If both --test-name-pattern and --test-skip-pattern are supplied, tests must satisfy both requirements in order to be executed.

--test-timeout#

A number of milliseconds the test execution will fail after. If unspecified, subtests inherit this value from their parent. The default value is Infinity.

--test-update-snapshots#

Regenerates the snapshot files used by the test runner for snapshot testing.

--throw-deprecation#

Throw errors for deprecations.

--title=title#

Set process.title on startup.

--tls-cipher-list=list#

Specify an alternative default TLS cipher list. Requires Node.js to be built with crypto support (default).

--tls-keylog=file#

Log TLS key material to a file. The key material is in NSS SSLKEYLOGFILE format and can be used by software (such as Wireshark) to decrypt the TLS traffic.

--tls-max-v1.2#

Set tls.DEFAULT_MAX_VERSION to 'TLSv1.2'. Use to disable support for TLSv1.3.

--tls-max-v1.3#

Set default tls.DEFAULT_MAX_VERSION to 'TLSv1.3'. Use to enable support for TLSv1.3.

--tls-min-v1.0#

Set default tls.DEFAULT_MIN_VERSION to 'TLSv1'. Use for compatibility with old TLS clients or servers.

--tls-min-v1.1#

Set default tls.DEFAULT_MIN_VERSION to 'TLSv1.1'. Use for compatibility with old TLS clients or servers.

--tls-min-v1.2#

Set default tls.DEFAULT_MIN_VERSION to 'TLSv1.2'. This is the default for 12.x and later, but the option is supported for compatibility with older Node.js versions.

--tls-min-v1.3#

Set default tls.DEFAULT_MIN_VERSION to 'TLSv1.3'. Use to disable support for TLSv1.2, which is not as secure as TLSv1.3.

--trace-deprecation#

Print stack traces for deprecations.

--trace-env#

Print information about any access to environment variables done in the current Node.js instance to stderr, including:

  • The environment variable reads that Node.js does internally.
  • Writes in the form of process.env.KEY = "SOME VALUE".
  • Reads in the form of process.env.KEY.
  • Definitions in the form of Object.defineProperty(process.env, 'KEY', {...}).
  • Queries in the form of Object.hasOwn(process.env, 'KEY'), process.env.hasOwnProperty('KEY') or 'KEY' in process.env.
  • Deletions in the form of delete process.env.KEY.
  • Enumerations inf the form of ...process.env or Object.keys(process.env).

Only the names of the environment variables being accessed are printed. The values are not printed.

To print the stack trace of the access, use --trace-env-js-stack and/or --trace-env-native-stack.

--trace-env-js-stack#

In addition to what --trace-env does, this prints the JavaScript stack trace of the access.

--trace-env-native-stack#

In addition to what --trace-env does, this prints the native stack trace of the access.

--trace-event-categories#

A comma separated list of categories that should be traced when trace event tracing is enabled using --trace-events-enabled.

--trace-event-file-pattern#

Template string specifying the filepath for the trace event data, it supports ${rotation} and ${pid}.

--trace-events-enabled#

Enables the collection of trace event tracing information.

--trace-exit#

Prints a stack trace whenever an environment is exited proactively, i.e. invoking process.exit().

--trace-require-module=mode#

Prints information about usage of Loading ECMAScript modules using require().

When mode is all, all usage is printed. When mode is no-node-modules, usage from the node_modules folder is excluded.

--trace-sigint#

Prints a stack trace on SIGINT.

--trace-sync-io#

Prints a stack trace whenever synchronous I/O is detected after the first turn of the event loop.

--trace-tls#

Prints TLS packet trace information to stderr. This can be used to debug TLS connection problems.

--trace-uncaught#

Print stack traces for uncaught exceptions; usually, the stack trace associated with the creation of an Error is printed, whereas this makes Node.js also print the stack trace associated with throwing the value (which does not need to be an Error instance).

Enabling this option may affect garbage collection behavior negatively.

--trace-warnings#

Print stack traces for process warnings (including deprecations).

--track-heap-objects#

Track heap object allocations for heap snapshots.

--unhandled-rejections=mode#

Using this flag allows to change what should happen when an unhandled rejection occurs. One of the following modes can be chosen:

  • throw: Emit unhandledRejection. If this hook is not set, raise the unhandled rejection as an uncaught exception. This is the default.
  • strict: Raise the unhandled rejection as an uncaught exception. If the exception is handled, unhandledRejection is emitted.
  • warn: Always trigger a warning, no matter if the unhandledRejection hook is set or not but do not print the deprecation warning.
  • warn-with-error-code: Emit unhandledRejection. If this hook is not set, trigger a warning, and set the process exit code to 1.
  • none: Silence all warnings.

If a rejection happens during the command line entry point's ES module static loading phase, it will always raise it as an uncaught exception.

--use-bundled-ca, --use-openssl-ca#

Use bundled Mozilla CA store as supplied by current Node.js version or use OpenSSL's default CA store. The default store is selectable at build-time.

The bundled CA store, as supplied by Node.js, is a snapshot of Mozilla CA store that is fixed at release time. It is identical on all supported platforms.

Using OpenSSL store allows for external modifications of the store. For most Linux and BSD distributions, this store is maintained by the distribution maintainers and system administrators. OpenSSL CA store location is dependent on configuration of the OpenSSL library but this can be altered at runtime using environment variables.

See SSL_CERT_DIR and SSL_CERT_FILE.

--use-env-proxy#

Stability: 1.1 - Active Development

When enabled, Node.js parses the HTTP_PROXY, HTTPS_PROXY and NO_PROXY environment variables during startup, and routes requests through the specified proxy.

Use this only with proxies that are trusted and authorized for the deployment. Proxy support is intended for reaching external networks through authorized proxy servers, for example when a firewall requires one. It is not for hiding traffic or evading network policy. See Built-in Proxy Support.

This is equivalent to setting the NODE_USE_ENV_PROXY=1 environment variable. When both are set, --use-env-proxy takes precedence.

--use-largepages=mode#

This option is no longer supported and a no-op. It used to re-map the Node.js static code to large memory pages at startup.

It still accepts the following values for compatibility:

  • off: No mapping will be attempted. This is the default.
  • on: No mapping will be attempted and a message will be printed to standard error stating it's no longer supported.
  • silent: Same as off.

--use-system-ca#

Node.js uses the trusted CA certificates present in the system store along with the --use-bundled-ca option and the NODE_EXTRA_CA_CERTS environment variable. On platforms other than Windows and macOS, this loads certificates from the directory and file trusted by OpenSSL, similar to --use-openssl-ca, with the difference being that it caches the certificates after first load.

On Windows and macOS, the certificate trust policy is similar to Chromium's policy for locally trusted certificates, but with some differences:

On macOS, the following settings are respected:

  • Default and System Keychains
    • Trust:
      • Any certificate where the “When using this certificate” flag is set to “Always Trust” or
      • Any certificate where the “Secure Sockets Layer (SSL)” flag is set to “Always Trust”.
    • The certificate must also be valid, with "X.509 Basic Policy" set to “Always Trust”.

On Windows, the following settings are respected:

  • Local Machine (accessed via certlm.msc)
    • Trust:
      • Trusted Root Certification Authorities
      • Trusted People
      • Enterprise Trust -> Enterprise -> Trusted Root Certification Authorities
      • Enterprise Trust -> Enterprise -> Trusted People
      • Enterprise Trust -> Group Policy -> Trusted Root Certification Authorities
      • Enterprise Trust -> Group Policy -> Trusted People
  • Current User (accessed via certmgr.msc)
    • Trust:
      • Trusted Root Certification Authorities
      • Enterprise Trust -> Group Policy -> Trusted Root Certification Authorities

On Windows and macOS, Node.js would check that the user settings for the trusted certificates do not forbid them for TLS server authentication before using them.

Node.js currently does not support distrust/revocation of certificates from another source based on system settings.

On other systems, Node.js loads certificates from the default certificate file (typically /etc/ssl/cert.pem) and default certificate directory (typically /etc/ssl/certs) that the version of OpenSSL that Node.js links to respects. This typically works with the convention on major Linux distributions and other Unix-like systems. If the overriding OpenSSL environment variables (typically SSL_CERT_FILE and SSL_CERT_DIR, depending on the configuration of the OpenSSL that Node.js links to) are set, the specified paths will be used to load certificates instead. These environment variables can be used as workarounds if the conventional paths used by the version of OpenSSL Node.js links to are not consistent with the system configuration that the users have for some reason.

--v8-options#

Print V8 command-line options.

--v8-pool-size=num#

Set V8's thread pool size which will be used to allocate background jobs.

If set to 0 then Node.js will choose an appropriate size of the thread pool based on an estimate of the amount of parallelism.

The amount of parallelism refers to the number of computations that can be carried out simultaneously in a given machine. In general, it's the same as the amount of CPUs, but it may diverge in environments such as VMs or containers.

-v, --version#

Print node's version.

--vfs-load=source#

  • source <string> A directory or an archive file to mount and run.

Requires --experimental-vfs. May be given at most once.

Mounts source as a virtual file system (node:vfs), and runs the entry point and all subsequent require()/import resolution against that mount rather than the real file system. The mount is placed at a reserved mount point assigned by Node.js, so it never shadows real paths and no target can be chosen. The entry point is taken from the mount the same way node <directory> takes one: the mount's own package.json "main", or index.js. Any positional command-line argument is the program's own (available from process.argv[2] onward), never an entry-point override.

process.argv[1] reports source rather than the reserved mount point, since the mount point is an opaque implementation detail.

The provider backing a source is chosen from the source itself rather than from its file name:

  • A directory is mounted with a RealFSProvider rooted there.
  • A file whose bytes are a ZIP archive is mounted with a ZipProvider, so an archive can carry any name.

Providers registered with vfs.registerProvider() (typically from a module preloaded with --require or --import) are consulted first, in reverse registration order, and may claim directories as well as files. If no provider claims the source, Node.js exits with an error.

In worker threads --vfs-load mounts but does not load: a worker inherits the mount and runs its own entry point, which may itself live in the mount.

The source is mounted at the same reserved mount point in every thread that mounts it, whatever else that thread mounts, so a path into the mount means the same thing in all of them.

A worker created with its own execArgv inherits none of the parent's options, and so does not mount the source at all. To run a script from the mount, such a worker must be given the same options again, --experimental-vfs and --vfs-load; without them, that thread has no mount for the script to come from, and the worker fails to load it. --experimental-vfs is also what makes node:vfs available to the worker's own code. A worker whose script comes from anywhere else, such as the real file system, needs nothing added.

--vfs-load is not permitted in NODE_OPTIONS: which entry point runs is the command line's decision, and the environment must not be able to redirect it.

$ node --experimental-vfs --vfs-load=app.zip
console

--watch#

Starts Node.js in watch mode. When in watch mode, changes in the watched files cause the Node.js process to restart. By default, watch mode will watch the entry point and any required or imported module. Use --watch-path to specify what paths to watch.

This flag cannot be combined with --check, --eval, --interactive, or the REPL.

Note: The --watch flag requires a file path as an argument and is incompatible with --run or inline script input, as --run takes precedence and ignores watch mode. If no file is provided, Node.js will exit with status code 9.

node --watch index.js
bash

--watch-kill-signal#

Stability: 1.1 - Active Development

Customizes the signal sent to the process on watch mode restarts.

node --watch --watch-kill-signal SIGINT test.js
bash

--watch-path#

Starts Node.js in watch mode and specifies what paths to watch. When in watch mode, changes in the watched paths cause the Node.js process to restart. This will turn off watching of required or imported modules, even when used in combination with --watch.

This flag cannot be combined with --check, --eval, --interactive, --test, or the REPL.

Note: Using --watch-path implicitly enables --watch, which requires a file path and is incompatible with --run, as --run takes precedence and ignores watch mode.

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

This option is only supported on macOS and Windows. An ERR_FEATURE_UNAVAILABLE_ON_PLATFORM exception will be thrown when the option is used on a platform that does not support it.

--watch-preserve-output#

Disable the clearing of the console when watch mode restarts the process.

node --watch --watch-preserve-output test.js
bash

--zero-fill-buffers#

Automatically zero-fills all newly allocated Buffer instances.

Environment variables#

Stability: 2 - Stable

FORCE_COLOR=[1, 2, 3]#

The FORCE_COLOR environment variable is used to enable ANSI colorized output. The value may be:

  • 1, true, or the empty string '' indicate 16-color support,
  • 2 to indicate 256-color support, or
  • 3 to indicate 16 million-color support.

When FORCE_COLOR is used and set to a supported value, both the NO_COLOR, and NODE_DISABLE_COLORS environment variables are ignored.

Any other value will result in colorized output being disabled.

NODE_COMPILE_CACHE=dir#

Enable the module compile cache for the Node.js instance. See the documentation of module compile cache for details.

NODE_COMPILE_CACHE_PORTABLE=1#

When set to 1, the module compile cache can be reused across different directory locations as long as the module layout relative to the cache directory remains the same, and by any user (the cache subdirectory is not suffixed with the creating user's uid).

NODE_COMPILE_CACHE_READONLY=1#

When set to 1, the module compile cache only reads existing entries from its directory: nothing is written to it and it is not created if missing.

NODE_DEBUG=module[,…]#

','-separated list of core modules that should print debug information.

NODE_DEBUG_NATIVE=module[,…]#

','-separated list of core C++ modules that should print debug information.

NODE_DISABLE_COLORS=1#

When set, colors will not be used in the REPL.

NODE_DISABLE_COMPILE_CACHE=1#

Stability: 1.1 - Active Development

Disable the module compile cache for the Node.js instance. See the documentation of module compile cache for details.

NODE_EXTRA_CA_CERTS=file#

When set, the well known "root" CAs (like VeriSign) will be extended with the extra certificates in file. The file should consist of one or more trusted certificates in PEM format. A message will be emitted (once) with process.emitWarning() if the file is missing or malformed, but any errors are otherwise ignored.

Neither the well known nor extra certificates are used when the ca options property is explicitly specified for a TLS or HTTPS client or server.

This environment variable is ignored when node runs as setuid root or has Linux file capabilities set.

The NODE_EXTRA_CA_CERTS environment variable is only read when the Node.js process is first launched. Changing the value at runtime using process.env.NODE_EXTRA_CA_CERTS has no effect on the current process.

NODE_ICU_DATA=file#

Data path for ICU (Intl object) data. Will extend linked-in data when compiled with small-icu support.

NODE_NO_WARNINGS=1#

When set to 1, process warnings are silenced.

NODE_OPTIONS=options...#

A space-separated list of command-line options. options... are interpreted before command-line options, so command-line options will override or compound after anything in options.... Node.js will exit with an error if an option that is not allowed in the environment is used, such as -p or a script file.

If an option value contains a space, it can be escaped using double quotes:

NODE_OPTIONS='--require "./my path/file.js"'
bash

A singleton flag passed as a command-line option will override the same flag passed into NODE_OPTIONS:

# The inspector will be available on port 5555
NODE_OPTIONS='--inspect=localhost:4444' node --inspect=localhost:5555
bash

A flag that can be passed multiple times will be treated as if its NODE_OPTIONS instances were passed first, and then its command-line instances afterwards:

NODE_OPTIONS='--require "./a.js"' node --require "./b.js"
# is equivalent to:
node --require "./a.js" --require "./b.js"
bash

Node.js options that are allowed are in the following list. If an option supports both --XX and --no-XX variants, they are both supported but only one is included in the list below.

  • --allow-addons
  • --allow-child-process
  • --allow-ffi
  • --allow-fs-read
  • --allow-fs-vfs
  • --allow-fs-write
  • --allow-inspector
  • --allow-net
  • --allow-openssl-store
  • --allow-wasi
  • --allow-worker
  • --bench-isolation
  • --bench-name-pattern
  • --bench-reporter-destination
  • --bench-reporter
  • --bench-samples
  • --bench-warmup
  • --conditions, -C
  • --cpu-prof-dir
  • --cpu-prof-interval
  • --cpu-prof-name
  • --cpu-prof
  • --diagnostic-dir
  • --disable-proto
  • --disable-sigusr1
  • --disable-warning
  • --disable-wasm-trap-handler
  • --dns-result-order
  • --enable-fips-indicator-events
  • --enable-fips
  • --enable-network-family-autoselection
  • --enable-source-maps
  • --entry-url
  • --experimental-abortcontroller
  • --experimental-addon-modules
  • --experimental-bench
  • --experimental-detect-module
  • --experimental-dtls
  • --experimental-eventsource
  • --experimental-import-meta-resolve
  • --experimental-import-text
  • --experimental-json-modules
  • --experimental-loader
  • --experimental-modules
  • --experimental-package-map
  • --experimental-print-required-tla
  • --experimental-quic
  • --experimental-require-module
  • --experimental-shadow-realm
  • --experimental-specifier-resolution
  • --experimental-stream-iter
  • --experimental-test-isolation
  • --experimental-top-level-await
  • --experimental-vfs
  • --experimental-vm-modules
  • --experimental-wasi-unstable-preview1
  • --experimental-web-worker
  • --force-context-aware
  • --force-fips
  • --force-node-api-uncaught-exceptions-policy
  • --frozen-intrinsics
  • --heap-prof-dir
  • --heap-prof-interval
  • --heap-prof-name
  • --heap-prof
  • --heapsnapshot-near-heap-limit
  • --heapsnapshot-signal
  • --http-parser
  • --icu-data-dir
  • --import
  • --input-type
  • --insecure-http-parser
  • --inspect-brk
  • --inspect-port, --debug-port
  • --inspect-publish-uid
  • --inspect-wait
  • --inspect
  • --localstorage-file
  • --max-http-header-size
  • --max-old-space-size-percentage
  • --network-family-autoselection-attempt-timeout
  • --no-addons
  • --no-async-context-frame
  • --no-deprecation
  • --no-experimental-ffi
  • --no-experimental-global-navigator
  • --no-experimental-repl-await
  • --no-experimental-sqlite
  • --no-experimental-strip-types
  • --no-experimental-websocket
  • --no-experimental-webstorage
  • --no-extra-info-on-fatal-exception
  • --no-force-async-hooks-checks
  • --no-global-search-paths
  • --no-network-family-autoselection
  • --no-strip-types
  • --no-warnings
  • --no-webstorage
  • --no-worker-snapshot
  • --node-memory-debug
  • --openssl-config
  • --openssl-legacy-provider
  • --openssl-shared-config
  • --pending-deprecation
  • --permission-audit
  • --permission
  • --preserve-symlinks-main
  • --preserve-symlinks
  • --prof-process
  • --redirect-warnings
  • --report-compact
  • --report-dir, --report-directory
  • --report-exclude-env
  • --report-exclude-network
  • --report-filename
  • --report-on-fatalerror
  • --report-on-signal
  • --report-signal
  • --report-uncaught-exception
  • --require-module
  • --require, -r
  • --secure-heap-min
  • --secure-heap
  • --snapshot-blob
  • --test-coverage-branches
  • --test-coverage-exclude
  • --test-coverage-functions
  • --test-coverage-include-all
  • --test-coverage-include
  • --test-coverage-lines
  • --test-global-setup
  • --test-isolation
  • --test-name-pattern
  • --test-only
  • --test-random-seed
  • --test-randomize
  • --test-reporter-destination
  • --test-reporter
  • --test-rerun-failures
  • --test-shard
  • --test-skip-pattern
  • --throw-deprecation
  • --title
  • --tls-cipher-list
  • --tls-keylog
  • --tls-max-v1.2
  • --tls-max-v1.3
  • --tls-min-v1.0
  • --tls-min-v1.1
  • --tls-min-v1.2
  • --tls-min-v1.3
  • --trace-deprecation
  • --trace-env-js-stack
  • --trace-env-native-stack
  • --trace-env
  • --trace-event-categories
  • --trace-event-file-pattern
  • --trace-events-enabled
  • --trace-exit
  • --trace-require-module
  • --trace-sigint
  • --trace-sync-io
  • --trace-tls
  • --trace-uncaught
  • --trace-warnings
  • --track-heap-objects
  • --unhandled-rejections
  • --use-bundled-ca
  • --use-env-proxy
  • --use-largepages
  • --use-openssl-ca
  • --use-system-ca
  • --v8-pool-size
  • --watch-kill-signal
  • --watch-path
  • --watch-preserve-output
  • --watch
  • --zero-fill-buffers

V8 options that are allowed are:

  • --abort-on-uncaught-exception
  • --disallow-code-generation-from-strings
  • --enable-etw-stack-walking
  • --expose-gc
  • --interpreted-frames-native-stack
  • --jitless
  • --max-heap-size
  • --max-old-space-size
  • --max-semi-space-size
  • --perf-basic-prof-only-functions
  • --perf-basic-prof
  • --perf-prof-unwinding-info
  • --perf-prof
  • --stack-trace-limit

--perf-basic-prof-only-functions, --perf-basic-prof, --perf-prof-unwinding-info, and --perf-prof are only available on Linux.

--enable-etw-stack-walking is only available on Windows.

NODE_PATH=path[:…]#

':'-separated list of directories prefixed to the module search path.

On Windows, this is a ';'-separated list instead.

NODE_PENDING_DEPRECATION=1#

When set to 1, emit pending deprecation warnings.

Pending deprecations are generally identical to a runtime deprecation with the notable exception that they are turned off by default and will not be emitted unless either the --pending-deprecation command-line flag, or the NODE_PENDING_DEPRECATION=1 environment variable, is set. Pending deprecations are used to provide a kind of selective "early warning" mechanism that developers may leverage to detect deprecated API usage.

NODE_PENDING_PIPE_INSTANCES=instances#

Set the number of pending pipe instance handles when the pipe server is waiting for connections. This setting applies to Windows only.

NODE_PRESERVE_SYMLINKS=1#

When set to 1, instructs the module loader to preserve symbolic links when resolving and caching modules.

NODE_REDIRECT_WARNINGS=file#

When set, process warnings will be emitted to the given file instead of printing to stderr. The file will be created if it does not exist, and will be appended to if it does. If an error occurs while attempting to write the warning to the file, the warning will be written to stderr instead. This is equivalent to using the --redirect-warnings=file command-line flag.

NODE_REPL_EXTERNAL_MODULE=file#

Path to a Node.js module which will be loaded in place of the built-in REPL. Overriding this value to an empty string ('') will use the built-in REPL.

NODE_REPL_HISTORY=file#

Path to the file used to store the persistent REPL history. The default path is ~/.node_repl_history, which is overridden by this variable. Setting the value to an empty string ('' or ' ') disables persistent REPL history.

NODE_SKIP_PLATFORM_CHECK=value#

If value equals '1', the check for a supported platform is skipped during Node.js startup. Node.js might not execute correctly. Any issues encountered on unsupported platforms will not be fixed.

NODE_TEST_CONTEXT=value#

If value equals 'child', test reporter options will be overridden and test output will be sent to stdout in the TAP format. If any other value is provided, Node.js makes no guarantees about the reporter format used or its stability.

NODE_TLS_REJECT_UNAUTHORIZED=value#

If value equals '0', certificate validation is disabled for TLS connections. This makes TLS, and HTTPS by extension, insecure. The use of this environment variable is strongly discouraged.

NODE_USE_ENV_PROXY=1#

Stability: 1.1 - Active Development

When enabled, Node.js parses the HTTP_PROXY, HTTPS_PROXY and NO_PROXY environment variables during startup, and routes requests through the specified proxy.

Use this only with proxies that are trusted and authorized for the deployment. Proxy support is intended for reaching external networks through authorized proxy servers, for example when a firewall requires one. It is not for hiding traffic or evading network policy. See Built-in Proxy Support.

This can also be enabled using the --use-env-proxy command-line flag. When both are set, --use-env-proxy takes precedence.

NODE_USE_SYSTEM_CA=1#

Node.js uses the trusted CA certificates present in the system store along with the --use-bundled-ca option and the NODE_EXTRA_CA_CERTS environment variable.

This can also be enabled using the --use-system-ca command-line flag. When both are set, --use-system-ca takes precedence.

NODE_V8_COVERAGE=dir#

When set, Node.js will begin outputting V8 JavaScript code coverage and Source Map data to the directory provided as an argument (coverage information is written as JSON to files with a coverage prefix).

NODE_V8_COVERAGE will automatically propagate to subprocesses, making it easier to instrument applications that call the child_process.spawn() family of functions. NODE_V8_COVERAGE can be set to an empty string, to prevent propagation.

Coverage output#

Coverage is output as an array of ScriptCoverage objects on the top-level key result:

{
  "result": [
    {
      "scriptId": "67",
      "url": "internal/tty.js",
      "functions": []
    }
  ]
}
json
Source map cache#

Stability: 1 - Experimental

If found, source map data is appended to the top-level key source-map-cache on the JSON coverage object.

source-map-cache is an object with keys representing the files source maps were extracted from, and values which include the raw source-map URL (in the key url), the parsed Source Map v3 information (in the key data), and the line lengths of the source file (in the key lineLengths).

{
  "result": [
    {
      "scriptId": "68",
      "url": "file:///absolute/path/to/source.js",
      "functions": []
    }
  ],
  "source-map-cache": {
    "file:///absolute/path/to/source.js": {
      "url": "./path-to-map.json",
      "data": {
        "version": 3,
        "sources": [
          "file:///absolute/path/to/original.js"
        ],
        "names": [
          "Foo",
          "console",
          "info"
        ],
        "mappings": "MAAMA,IACJC,YAAaC",
        "sourceRoot": "./"
      },
      "lineLengths": [
        13,
        62,
        38,
        27
      ]
    }
  }
}
json

NO_COLOR=<any>#

NO_COLOR is an alias for NODE_DISABLE_COLORS. The value of the environment variable is arbitrary.

OPENSSL_CONF=file#

Load an OpenSSL configuration file on startup. The file can be used as part of a FIPS mode configuration.

If the variable is set to an empty value, Node.js starts without loading any OpenSSL configuration file. This is a way past a default configuration file that exists but cannot be read, for example when /etc/ssl is not accessible to the user Node.js runs as, which is otherwise fatal at startup. No configuration is applied in that case, including any FIPS mode setup the file would have performed.

If the --openssl-config command-line option is used, the environment variable is ignored, and an empty value has no effect.

SSL_CERT_DIR=dir#

If --use-openssl-ca is enabled, or if --use-system-ca is enabled on platforms other than macOS and Windows, this overrides and sets OpenSSL's directory containing trusted certificates.

Be aware that unless the child environment is explicitly set, this environment variable will be inherited by any child processes, and if they use OpenSSL, it may cause them to trust the same CAs as node.

SSL_CERT_FILE=file#

If --use-openssl-ca is enabled, or if --use-system-ca is enabled on platforms other than macOS and Windows, this overrides and sets OpenSSL's file containing trusted certificates.

Be aware that unless the child environment is explicitly set, this environment variable will be inherited by any child processes, and if they use OpenSSL, it may cause them to trust the same CAs as node.

TZ#

The TZ environment variable is used to specify the timezone configuration.

While Node.js does not support all of the various ways that TZ is handled in other environments, it does support basic timezone IDs (such as 'Etc/UTC', 'Europe/Paris', or 'America/New_York'). It may support a few other abbreviations or aliases, but these are strongly discouraged and not guaranteed.

$ TZ=Europe/Dublin node -pe "new Date().toString()"
Wed May 12 2021 20:30:48 GMT+0100 (Irish Standard Time)
console

UV_THREADPOOL_SIZE=size#

Set the number of threads used in libuv's threadpool to size threads.

Asynchronous system APIs are used by Node.js whenever possible, but where they do not exist, libuv's threadpool is used to create asynchronous node APIs based on synchronous system APIs. Node.js APIs that use the threadpool are:

  • all fs APIs, other than the file watcher APIs and those that are explicitly synchronous
  • asynchronous crypto APIs such as crypto.pbkdf2(), crypto.scrypt(), crypto.randomBytes(), crypto.randomFill(), crypto.generateKeyPair()
  • dns.lookup()
  • all zlib APIs, other than those that are explicitly synchronous

Because libuv's threadpool has a fixed size, it means that if for whatever reason any of these APIs takes a long time, other (seemingly unrelated) APIs that run in libuv's threadpool will experience degraded performance. In order to mitigate this issue, one potential solution is to increase the size of libuv's threadpool by setting the 'UV_THREADPOOL_SIZE' environment variable to a value greater than 4 (its current default value). However, setting this from inside the process using process.env.UV_THREADPOOL_SIZE=size is not guaranteed to work as the threadpool would have been created as part of the runtime initialisation much before user code is run. For more information, see the libuv threadpool documentation.

Useful V8 options#

V8 has its own set of CLI options. Any V8 CLI option that is provided to node will be passed on to V8 to handle. V8's options have no stability guarantee. The V8 team themselves don't consider them to be part of their formal API, and reserve the right to change them at any time. Likewise, they are not covered by the Node.js stability guarantees. Many of the V8 options are of interest only to V8 developers. Despite this, there is a small set of V8 options that are widely applicable to Node.js, and they are documented here:

--abort-on-uncaught-exception#

--disallow-code-generation-from-strings#

--enable-etw-stack-walking#

--expose-gc#

--harmony-shadow-realm#

--heap-snapshot-on-oom#

--interpreted-frames-native-stack#

--jitless#

--max-heap-size#

Specifies the maximum heap size (in megabytes) for the process.

This option is typically used to limit the amount of memory the process can use for its JavaScript heap.

--max-old-space-size=SIZE (in MiB)#

Sets the max memory size of V8's old memory section. As memory consumption approaches the limit, V8 will spend more time on garbage collection in an effort to free unused memory.

On a machine with 2 GiB of memory, consider setting this to 1536 (1.5 GiB) to leave some memory for other uses and avoid swapping.

node --max-old-space-size=1536 index.js
bash

--max-semi-space-size=SIZE (in MiB)#

Sets the maximum semi-space size for V8's scavenge garbage collector in MiB (mebibytes). Increasing the max size of a semi-space may improve throughput for Node.js at the cost of more memory consumption.

Since the young generation size of the V8 heap is three times (see YoungGenerationSizeFromSemiSpaceSize in V8) the size of the semi-space, an increase of 1 MiB to semi-space applies to each of the three individual semi-spaces and causes the heap size to increase by 3 MiB. The throughput improvement depends on your workload (see #42511).

The default value depends on the memory limit. For example, on 64-bit systems with a memory limit of 512 MiB, the max size of a semi-space defaults to 1 MiB. For memory limits up to and including 2GiB, the default max size of a semi-space will be less than 16 MiB on 64-bit systems.

To get the best configuration for your application, you should try different max-semi-space-size values when running benchmarks for your application.

For example, benchmark on a 64-bit systems:

for MiB in 16 32 64 128; do
    node --max-semi-space-size=$MiB index.js
done
bash

--perf-basic-prof#

--perf-basic-prof-only-functions#

--perf-prof#

--perf-prof-unwinding-info#

--prof#

--security-revert#

--stack-trace-limit=limit#

The maximum number of stack frames to collect in an error's stack trace. Setting it to 0 disables stack trace collection. The default value is 10.

node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
bash

Console#

Stability: 2 - Stable

The node:console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error(), and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without calling require('node:console').

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. Programs that desire to depend on the synchronous / asynchronous behavior of the console functions should first figure out the nature of console's backing stream. This is because the stream is dependent on the underlying platform and standard stream configuration of the current process. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
//   Error: Whoops, something bad happened
//     at [eval]:5:15
//     at Script.runInThisContext (node:vm:132:18)
//     at Object.runInThisContext (node:vm:309:38)
//     at node:internal/process/execution:77:19
//     at [eval]-wrapper:6:22
//     at evalScript (node:internal/process/execution:76:60)
//     at node:internal/main/eval_string:23:3

const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
js

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);

myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
js

Class: Console#

The Console class can be used to create a simple logger with configurable output streams and can be accessed using either require('node:console').Console or console.Console (or their destructured counterparts):

import { Console } from 'node:console';
const { Console } = require('node:console');
javascript
const { Console } = console;
js

new Console(stdout[, stderr][, ignoreErrors])#

new Console(options)#

  • options <Object>
    • stdout <stream.Writable>
    • stderr <stream.Writable>
    • ignoreErrors <boolean> Ignore errors when writing to the underlying streams. Default: true.
    • colorMode <boolean> | <string> Set color support for this Console instance. Setting to true enables coloring while inspecting values. Setting to false disables coloring while inspecting values. Setting to 'auto' makes color support depend on the value of the isTTY property and the value returned by getColorDepth() on the respective stream. This option can not be used, if inspectOptions.colors is set as well. Default: 'auto'.
    • inspectOptions <Object> | <Map> Specifies options that are passed along to util.inspect(). Can be an options object or, if different options for stdout and stderr are desired, a Map from stream objects to options.
    • groupIndentation <number> Set group indentation. Default: 2.

Creates a new Console with one or two writable stream instances. stdout is a writable stream to print log or info output. stderr is used for warning or error output. If stderr is not provided, stdout is used for stderr.

import { createWriteStream } from 'node:fs';
import { Console } from 'node:console';
// Alternatively
// const { Console } = console;

const output = createWriteStream('./stdout.log');
const errorOutput = createWriteStream('./stderr.log');
// Custom simple logger
const logger = new Console({ stdout: output, stderr: errorOutput });
// use it like console
const count = 5;
logger.log('count: %d', count);
// In stdout.log: count 5
const fs = require('node:fs');
const { Console } = require('node:console');
// Alternatively
// const { Console } = console;

const output = fs.createWriteStream('./stdout.log');
const errorOutput = fs.createWriteStream('./stderr.log');
// Custom simple logger
const logger = new Console({ stdout: output, stderr: errorOutput });
// use it like console
const count = 5;
logger.log('count: %d', count);
// In stdout.log: count 5
javascript

The global console is a special Console whose output is sent to process.stdout and process.stderr. It is equivalent to calling:

new Console({ stdout: process.stdout, stderr: process.stderr });
js

console.assert(value[, ...message])#

  • value <any> The value tested for being truthy.
  • ...message <any> All arguments besides value are used as error message.

console.assert() writes a message if value is falsy or omitted. It only writes a message and does not otherwise affect execution. The output always starts with "Assertion failed". If provided, message is formatted using util.format().

If value is truthy, nothing happens.

console.assert(true, 'does nothing');

console.assert(false, 'Whoops %s work', 'didn\'t');
// Assertion failed: Whoops didn't work

console.assert();
// Assertion failed
js

console.clear()#

When stdout is a TTY, calling console.clear() will attempt to clear the TTY. When stdout is not a TTY, this method does nothing.

The specific operation of console.clear() can vary across operating systems and terminal types. For most Linux operating systems, console.clear() operates similarly to the clear shell command. On Windows, console.clear() will clear only the output in the current terminal viewport for the Node.js binary.

console.count([label])#

  • label <string> The display label for the counter. Default: 'default'.

Maintains an internal counter specific to label and outputs to stdout the number of times console.count() has been called with the given label.

> console.count()
default: 1
undefined
> console.count('default')
default: 2
undefined
> console.count('abc')
abc: 1
undefined
> console.count('xyz')
xyz: 1
undefined
> console.count('abc')
abc: 2
undefined
> console.count()
default: 3
undefined
>
console

console.countReset([label])#

  • label <string> The display label for the counter. Default: 'default'.

Resets the internal counter specific to label.

> console.count('abc');
abc: 1
undefined
> console.countReset('abc');
undefined
> console.count('abc');
abc: 1
undefined
>
console

console.debug(data[, ...args])#

The console.debug() function is an alias for console.log().

console.dir(obj[, options])#

  • obj <any>
  • options <Object>
    • showHidden <boolean> If true then the object's non-enumerable and symbol properties will be shown too. Default: false.
    • depth <number> Tells util.inspect() how many times to recurse while formatting the object. This is useful for inspecting large complicated objects. To make it recurse indefinitely, pass null. Default: 2.
    • colors <boolean> If true, then the output will be styled with ANSI color codes. Colors are customizable; see customizing util.inspect() colors. Default: false.

Uses util.inspect() on obj and prints the resulting string to stdout. This function bypasses any custom inspect() function defined on obj.

console.dirxml(...data)#

This method calls console.log() passing it the arguments received. This method does not produce any XML formatting.

console.error([data][, ...args])#

Prints to stderr with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const code = 5;
console.error('error #%d', code);
// Prints: error #5, to stderr
console.error('error', code);
// Prints: error 5, to stderr
js

If formatting elements (e.g. %d) are not found in the first string then util.inspect() is called on each argument and the resulting string values are concatenated. See util.format() for more information.

console.group([...label])#

Increases indentation of subsequent lines by spaces for groupIndentation length.

If one or more labels are provided, those are printed first without the additional indentation.

console.groupCollapsed()#

An alias for console.group().

console.groupEnd()#

Decreases indentation of subsequent lines by spaces for groupIndentation length.

console.info([data][, ...args])#

The console.info() function is an alias for console.log().

console.log([data][, ...args])#

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
js

See util.format() for more information.

console.table(tabularData[, properties])#

  • tabularData <any>
  • properties <string>[] Alternate properties for constructing the table.

Try to construct a table with the columns of the properties of tabularData (or use properties) and rows of tabularData and log it. Falls back to just logging the argument if it can't be parsed as tabular.

// These can't be parsed as tabular data
console.table(Symbol());
// Symbol()

console.table(undefined);
// undefined

console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }]);
// ┌─────────┬─────┬─────┐
// │ (index) │ a   │ b   │
// ├─────────┼─────┼─────┤
// │ 0       │ 1   │ 'Y' │
// │ 1       │ 'Z' │ 2   │
// └─────────┴─────┴─────┘

console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }], ['a']);
// ┌─────────┬─────┐
// │ (index) │ a   │
// ├─────────┼─────┤
// │ 0       │ 1   │
// │ 1       │ 'Z' │
// └─────────┴─────┘
js

console.time([label])#

Starts a timer that can be used to compute the duration of an operation. Timers are identified by a unique label. Use the same label when calling console.timeEnd() to stop the timer and output the elapsed time in suitable time units to stdout. For example, if the elapsed time is 3869ms, console.timeEnd() displays "3.869s".

console.timeEnd([label])#

Stops a timer that was previously started by calling console.time() and prints the result to stdout:

console.time('bunch-of-stuff');
// Do a bunch of stuff.
console.timeEnd('bunch-of-stuff');
// Prints: bunch-of-stuff: 225.438ms
js

console.timeLog([label][, ...data])#

For a timer that was previously started by calling console.time(), prints the elapsed time and other data arguments to stdout:

console.time('process');
const value = expensiveProcess1(); // Returns 42
console.timeLog('process', value);
// Prints "process: 365.227ms 42".
doExpensiveProcess2(value);
console.timeEnd('process');
js

console.trace([message][, ...args])#

Prints to stderr the string 'Trace: ', followed by the util.format() formatted message and stack trace to the current position in the code.

console.trace('Show me');
// Prints: (stack trace will vary based on where trace is called)
//  Trace: Show me
//    at repl:2:9
//    at REPLServer.defaultEval (repl.js:248:27)
//    at bound (domain.js:287:14)
//    at REPLServer.runBound [as eval] (domain.js:300:12)
//    at REPLServer.<anonymous> (repl.js:412:12)
//    at emitOne (events.js:82:20)
//    at REPLServer.emit (events.js:169:7)
//    at REPLServer.Interface._onLine (readline.js:210:10)
//    at REPLServer.Interface._line (readline.js:549:8)
//    at REPLServer.Interface._ttyWrite (readline.js:826:14)
js

console.warn([data][, ...args])#

The console.warn() function is an alias for console.error().

Inspector only methods#

The following methods are exposed by the V8 engine in the general API but do not display anything unless used in conjunction with the inspector (--inspect flag).

console.profile([label])#

This method does not display anything unless used in the inspector. The console.profile() method starts a JavaScript CPU profile with an optional label until console.profileEnd() is called. The profile is then added to the Profile panel of the inspector.

console.profile('MyLabel');
// Some code
console.profileEnd('MyLabel');
// Adds the profile 'MyLabel' to the Profiles panel of the inspector.
js

console.profileEnd([label])#

This method does not display anything unless used in the inspector. Stops the current JavaScript CPU profiling session if one has been started and prints the report to the Profiles panel of the inspector. See console.profile() for an example.

If this method is called without a label, the most recently started profile is stopped.

console.timeStamp([label])#

This method does not display anything unless used in the inspector. The console.timeStamp() method adds an event with the label 'label' to the Timeline panel of the inspector.

Crypto#

Stability: 2 - Stable

The node:crypto module provides cryptographic functionality that includes a set of wrappers for OpenSSL's hash, message authentication code (MAC), cipher, decipher, sign, verify, and key encapsulation mechanism (KEM) functions.

const { createHmac } = await import('node:crypto');

const secret = 'abcdefg';
const hash = createHmac('sha256', secret)
               .update('I love cupcakes')
               .digest('hex');
console.log(hash);
// Prints:
//   c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658e
const { createHmac } = require('node:crypto');

const secret = 'abcdefg';
const hash = createHmac('sha256', secret)
               .update('I love cupcakes')
               .digest('hex');
console.log(hash);
// Prints:
//   c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658e
javascript

Determining if crypto support is unavailable#

It is possible for Node.js to be built without including support for the node:crypto module. In such cases, attempting to import from crypto or calling require('node:crypto') will result in an error being thrown.

When using CommonJS, the error thrown can be caught using try/catch:

let crypto;
try {
  crypto = require('node:crypto');
} catch (err) {
  console.error('crypto support is disabled!');
}
cjs

When using the lexical ESM import keyword, the error can only be caught if a handler for process.on('uncaughtException') is registered before any attempt to load the module is made (using, for instance, a preload module).

When using ESM, if there is a chance that the code may be run on a build of Node.js where crypto support is not enabled, consider using the import() function instead of the lexical import keyword:

let crypto;
try {
  crypto = await import('node:crypto');
} catch (err) {
  console.error('crypto support is disabled!');
}
mjs

Asymmetric key types#

The following lists group the asymmetric key types recognized by the KeyObject API by the complete set of formats supported for importing and exporting each type.

Formats: 'pem', 'der'

  • 'dh' (Diffie-Hellman) — OID 1.2.840.113549.1.3.1
  • 'dsa' — OID 1.2.840.10040.4.1
  • 'rsa-pss' — OID 1.2.840.113549.1.1.10

Formats: 'pem', 'der', 'jwk'

  • 'rsa' — OID 1.2.840.113549.1.1.1

Formats: 'pem', 'der', 'jwk', 'raw-public', 'raw-private'

  • 'ec' (Elliptic curve) — OID 1.2.840.10045.2.1
  • 'ed25519' — OID 1.3.101.112
  • 'ed448' — OID 1.3.101.113
  • 'slh-dsa-sha2-128f'1 — OID 2.16.840.1.101.3.4.3.21
  • 'slh-dsa-sha2-128s'1 — OID 2.16.840.1.101.3.4.3.20
  • 'slh-dsa-sha2-192f'1 — OID 2.16.840.1.101.3.4.3.23
  • 'slh-dsa-sha2-192s'1 — OID 2.16.840.1.101.3.4.3.22
  • 'slh-dsa-sha2-256f'1 — OID 2.16.840.1.101.3.4.3.25
  • 'slh-dsa-sha2-256s'1 — OID 2.16.840.1.101.3.4.3.24
  • 'slh-dsa-shake-128f'1 — OID 2.16.840.1.101.3.4.3.27
  • 'slh-dsa-shake-128s'1 — OID 2.16.840.1.101.3.4.3.26
  • 'slh-dsa-shake-192f'1 — OID 2.16.840.1.101.3.4.3.29
  • 'slh-dsa-shake-192s'1 — OID 2.16.840.1.101.3.4.3.28
  • 'slh-dsa-shake-256f'1 — OID 2.16.840.1.101.3.4.3.31
  • 'slh-dsa-shake-256s'1 — OID 2.16.840.1.101.3.4.3.30
  • 'x25519' — OID 1.3.101.110
  • 'x448' — OID 1.3.101.111

Formats: 'pem', 'der', 'jwk', 'raw-public', 'raw-seed'

  • 'ml-dsa-44'1 — OID 2.16.840.1.101.3.4.3.17
  • 'ml-dsa-65'1 — OID 2.16.840.1.101.3.4.3.18
  • 'ml-dsa-87'1 — OID 2.16.840.1.101.3.4.3.19
  • 'ml-kem-512'1 — OID 2.16.840.1.101.3.4.4.1
  • 'ml-kem-768'1 — OID 2.16.840.1.101.3.4.4.2
  • 'ml-kem-1024'1 — OID 2.16.840.1.101.3.4.4.3

Key formats#

Asymmetric keys can be represented in several formats. The recommended approach is to import key material into a KeyObject once and reuse it for all subsequent operations, as this avoids repeated parsing and delivers the best performance.

When a KeyObject is not practical - for example, when key material arrives in a protocol message and is used only once - most cryptographic functions also accept a PEM string or an object specifying the format and key material directly. See crypto.createPublicKey(), crypto.createPrivateKey(), and keyObject.export() for the full options accepted by each format.

KeyObject#

A KeyObject is the in-memory representation of a parsed key. It is created by crypto.createPublicKey(), crypto.createPrivateKey(), crypto.createSecretKey(), or key generation functions such as crypto.generateKeyPair(). The first cryptographic operation with a given KeyObject may be slower than subsequent ones because OpenSSL lazily initializes internal caches on first use.

PEM and DER#

PEM and DER are the traditional encoding formats for asymmetric keys based on ASN.1 structures.

  • PEM is a text encoding that wraps Base64-encoded DER data between header and footer lines (e.g. -----BEGIN PUBLIC KEY-----). PEM strings can be passed directly to most cryptographic operations.
  • DER is the binary encoding of the same ASN.1 structures. When providing DER input, the type (typically 'spki' or 'pkcs8') must be specified explicitly.
JSON Web Key (JWK)#

JSON Web Key (JWK) is a JSON-based key representation defined in RFC 7517. JWK encodes each key component as an individual Base64url-encoded value inside a JSON object. For RSA keys, JWK avoids ASN.1 parsing overhead and is the fastest serialized import format.

Raw key formats#

Stability: 1.1 - Active development

The 'raw-public', 'raw-private', and 'raw-seed' key formats allow importing and exporting raw key material without any encoding wrapper. See keyObject.export(), crypto.createPublicKey(), and crypto.createPrivateKey() for usage details.

'raw-public' is generally the fastest way to import a public key. 'raw-private' and 'raw-seed' are not always faster than other formats because they only contain the private scalar or seed - importing them requires deriving the public key component (e.g. elliptic curve point multiplication or seed expansion), which can be expensive. Other formats include both private and public components, avoiding that computation.

Choosing a key format#

Always prefer a KeyObject - create one from whatever format you have and reuse it. The guidance below applies only when choosing between serialization formats, either for importing into a KeyObject or for passing key material inline when a KeyObject is not practical.

Importing keys#

When creating a KeyObject for repeated use, the import cost is paid once, so choosing a faster format reduces startup latency.

The import cost breaks down into two parts: parsing overhead (decoding the serialization wrapper) and key computation (any mathematical work needed to reconstruct the full key, such as deriving a public key from a private scalar or expanding a seed). Which part dominates depends on the key type. For example:

  • Public keys - 'raw-public' is the fastest serialized format because the raw format skips all ASN.1 and Base64 decoding.
  • EC private keys - 'raw-private' is faster than PEM or DER because it avoids ASN.1 parsing. However, for larger curves (e.g. P-384, P-521) the required derivation of the public point from the private scalar becomes expensive, reducing the advantage.
  • RSA keys - 'jwk' is the fastest serialized format. JWK represents RSA key components as individual Base64url-encoded integers, avoiding the overhead of ASN.1 parsing entirely.
Inline key material in operations#

When a KeyObject cannot be reused (e.g. the key arrives as raw bytes in a protocol message and is used only once), most cryptographic functions also accept a PEM string or an object specifying the format and key material directly. In this case the total cost is the sum of key import and the cryptographic computation itself.

For operations where the cryptographic computation dominates - such as signing with RSA or ECDH key agreement with P-384 or P-521 - the serialization format has negligible impact on overall throughput, so choose whichever format is most convenient. For lightweight operations like Ed25519 signing or verification, the import cost is a larger fraction of the total, so a faster format like 'raw-public' or 'raw-private' can meaningfully improve throughput.

Even if the same key material is used only a few times, it is worth importing it into a KeyObject rather than passing the raw or PEM representation repeatedly.

Examples#

Example: Reusing a KeyObject across sign and verify operations:

import { promisify } from 'node:util';
const { generateKeyPair, sign, verify } = await import('node:crypto');

const { publicKey, privateKey } = await promisify(generateKeyPair)('ed25519');

// A KeyObject holds the parsed key in memory and can be reused
// across multiple operations without re-parsing.
const data = new TextEncoder().encode('message to sign');
const signature = sign(null, data, privateKey);
verify(null, data, publicKey, signature);
mjs

Example: Importing keys of various formats into KeyObjects:

import { promisify } from 'node:util';
const {
  createPrivateKey, createPublicKey, generateKeyPair,
} = await import('node:crypto');

const generated = await promisify(generateKeyPair)('ed25519');

// PEM
const privatePem = generated.privateKey.export({ format: 'pem', type: 'pkcs8' });
const publicPem = generated.publicKey.export({ format: 'pem', type: 'spki' });
createPrivateKey(privatePem);
createPublicKey(publicPem);

// DER - requires explicit type
const privateDer = generated.privateKey.export({ format: 'der', type: 'pkcs8' });
const publicDer = generated.publicKey.export({ format: 'der', type: 'spki' });
createPrivateKey({ key: privateDer, format: 'der', type: 'pkcs8' });
createPublicKey({ key: publicDer, format: 'der', type: 'spki' });

// JWK
const privateJwk = generated.privateKey.export({ format: 'jwk' });
const publicJwk = generated.publicKey.export({ format: 'jwk' });
createPrivateKey({ key: privateJwk, format: 'jwk' });
createPublicKey({ key: publicJwk, format: 'jwk' });

// Raw
const rawPriv = generated.privateKey.export({ format: 'raw-private' });
const rawPub = generated.publicKey.export({ format: 'raw-public' });
createPrivateKey({ key: rawPriv, format: 'raw-private', asymmetricKeyType: 'ed25519' });
createPublicKey({ key: rawPub, format: 'raw-public', asymmetricKeyType: 'ed25519' });
mjs

Example: Passing key material directly to crypto.sign() and crypto.verify() without creating a KeyObject first:

import { promisify } from 'node:util';
const { generateKeyPair, sign, verify } = await import('node:crypto');

const generated = await promisify(generateKeyPair)('ed25519');

const data = new TextEncoder().encode('message to sign');

// PEM strings
const privatePem = generated.privateKey.export({ format: 'pem', type: 'pkcs8' });
const publicPem = generated.publicKey.export({ format: 'pem', type: 'spki' });
const sig1 = sign(null, data, privatePem);
verify(null, data, publicPem, sig1);

// JWK objects
const privateJwk = generated.privateKey.export({ format: 'jwk' });
const publicJwk = generated.publicKey.export({ format: 'jwk' });
const sig2 = sign(null, data, { key: privateJwk, format: 'jwk' });
verify(null, data, { key: publicJwk, format: 'jwk' }, sig2);

// Raw key bytes
const rawPriv = generated.privateKey.export({ format: 'raw-private' });
const rawPub = generated.publicKey.export({ format: 'raw-public' });
const sig3 = sign(null, data, {
  key: rawPriv, format: 'raw-private', asymmetricKeyType: 'ed25519',
});
verify(null, data, {
  key: rawPub, format: 'raw-public', asymmetricKeyType: 'ed25519',
}, sig3);
mjs

Example: For EC keys, the namedCurve option is required when importing raw keys:

import { promisify } from 'node:util';
const {
  createPrivateKey, createPublicKey, generateKeyPair, sign, verify,
} = await import('node:crypto');

const generated = await promisify(generateKeyPair)('ec', {
  namedCurve: 'P-256',
});

// Export the raw EC public key (uncompressed by default).
const rawPublicKey = generated.publicKey.export({ format: 'raw-public' });

// The following is equivalent.
const rawPublicKeyUncompressed = generated.publicKey.export({
  format: 'raw-public',
  type: 'uncompressed',
});

// Export compressed point format.
const rawPublicKeyCompressed = generated.publicKey.export({
  format: 'raw-public',
  type: 'compressed',
});

// Export the raw EC private key.
const rawPrivateKey = generated.privateKey.export({ format: 'raw-private' });

// Import the raw EC keys.
// Both compressed and uncompressed point formats are accepted.
const publicKey = createPublicKey({
  key: rawPublicKey,
  format: 'raw-public',
  asymmetricKeyType: 'ec',
  namedCurve: 'P-256',
});
const privateKey = createPrivateKey({
  key: rawPrivateKey,
  format: 'raw-private',
  asymmetricKeyType: 'ec',
  namedCurve: 'P-256',
});

const data = new TextEncoder().encode('message to sign');
const signature = sign('sha256', data, privateKey);
verify('sha256', data, publicKey, signature);
mjs

Example: Exporting raw seeds and importing them:

import { promisify } from 'node:util';
const {
  createPrivateKey, decapsulate, encapsulate, generateKeyPair,
} = await import('node:crypto');

const generated = await promisify(generateKeyPair)('ml-kem-768');

// Export the raw seed (64 bytes for ML-KEM).
const seed = generated.privateKey.export({ format: 'raw-seed' });

// Import the raw seed.
const privateKey = createPrivateKey({
  key: seed,
  format: 'raw-seed',
  asymmetricKeyType: 'ml-kem-768',
});

const { ciphertext } = encapsulate(generated.publicKey);
decapsulate(privateKey, ciphertext);
mjs

Class: Certificate#

SPKAC is a Certificate Signing Request mechanism originally implemented by Netscape and was specified formally as part of HTML5's keygen element.

<keygen> is deprecated since HTML 5.2 and new projects should not use this element anymore.

The node:crypto module provides the Certificate class for working with SPKAC data. The most common usage is handling output generated by the HTML5 <keygen> element. Node.js uses OpenSSL's SPKAC implementation internally.

Static method: Certificate.exportChallenge(spkac[, encoding])#

const { Certificate } = await import('node:crypto');
const spkac = getSpkacSomehow();
const challenge = Certificate.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// Prints: the challenge as a UTF8 string
const { Certificate } = require('node:crypto');
const spkac = getSpkacSomehow();
const challenge = Certificate.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// Prints: the challenge as a UTF8 string
javascript

Static method: Certificate.exportPublicKey(spkac[, encoding])#

const { Certificate } = await import('node:crypto');
const spkac = getSpkacSomehow();
const publicKey = Certificate.exportPublicKey(spkac);
console.log(publicKey);
// Prints: the public key as <Buffer ...>
const { Certificate } = require('node:crypto');
const spkac = getSpkacSomehow();
const publicKey = Certificate.exportPublicKey(spkac);
console.log(publicKey);
// Prints: the public key as <Buffer ...>
javascript

Static method: Certificate.verifySpkac(spkac[, encoding])#

import { Buffer } from 'node:buffer';
const { Certificate } = await import('node:crypto');

const spkac = getSpkacSomehow();
console.log(Certificate.verifySpkac(Buffer.from(spkac)));
// Prints: true or false
const { Buffer } = require('node:buffer');
const { Certificate } = require('node:crypto');

const spkac = getSpkacSomehow();
console.log(Certificate.verifySpkac(Buffer.from(spkac)));
// Prints: true or false
javascript

Legacy API#

Stability: 0 - Deprecated

As a legacy interface, it is possible to create new instances of the crypto.Certificate class as illustrated in the examples below.

new crypto.Certificate()#

Instances of the Certificate class can be created using the new keyword or by calling crypto.Certificate() as a function:

const { Certificate } = await import('node:crypto');

const cert1 = new Certificate();
const cert2 = Certificate();
const { Certificate } = require('node:crypto');

const cert1 = new Certificate();
const cert2 = Certificate();
javascript
certificate.exportChallenge(spkac[, encoding])#
const { Certificate } = await import('node:crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const challenge = cert.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// Prints: the challenge as a UTF8 string
const { Certificate } = require('node:crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const challenge = cert.exportChallenge(spkac);
console.log(challenge.toString('utf8'));
// Prints: the challenge as a UTF8 string
javascript
certificate.exportPublicKey(spkac[, encoding])#
const { Certificate } = await import('node:crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const publicKey = cert.exportPublicKey(spkac);
console.log(publicKey);
// Prints: the public key as <Buffer ...>
const { Certificate } = require('node:crypto');
const cert = Certificate();
const spkac = getSpkacSomehow();
const publicKey = cert.exportPublicKey(spkac);
console.log(publicKey);
// Prints: the public key as <Buffer ...>
javascript
certificate.verifySpkac(spkac[, encoding])#
import { Buffer } from 'node:buffer';
const { Certificate } = await import('node:crypto');

const cert = Certificate();
const spkac = getSpkacSomehow();
console.log(cert.verifySpkac(Buffer.from(spkac)));
// Prints: true or false
const { Buffer } = require('node:buffer');
const { Certificate } = require('node:crypto');

const cert = Certificate();
const spkac = getSpkacSomehow();
console.log(cert.verifySpkac(Buffer.from(spkac)));
// Prints: true or false
javascript

Class: Cipheriv#

Instances of the Cipheriv class are used to encrypt data. The class can be used in one of two ways:

  • As a stream that is both readable and writable, where plain unencrypted data is written to produce encrypted data on the readable side, or
  • Using the cipher.update() and cipher.final() methods to produce the encrypted data.

The crypto.createCipheriv() method is used to create Cipheriv instances. Cipheriv objects are not to be created directly using the new keyword.

The selected algorithm may impose additional restrictions on streaming and calls to cipher.update(). See CCM mode, CBC-CTS mode, XTS mode, AES key wrap modes, and SIV and GCM-SIV modes.

Example: Using Cipheriv objects as streams:

const {
  scrypt,
  randomFill,
  createCipheriv,
} = await import('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';

// First, we'll generate the key. The key length is dependent on the algorithm.
// In this case for aes192, it is 24 bytes (192 bits).
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // Then, we'll generate a random initialization vector
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    // Once we have the key and iv, we can create and use the cipher...
    const cipher = createCipheriv(algorithm, key, iv);

    let encrypted = '';
    cipher.setEncoding('hex');

    cipher.on('data', (chunk) => encrypted += chunk);
    cipher.on('end', () => console.log(encrypted));

    cipher.write('some clear text data');
    cipher.end();
  });
});
const {
  scrypt,
  randomFill,
  createCipheriv,
} = require('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';

// First, we'll generate the key. The key length is dependent on the algorithm.
// In this case for aes192, it is 24 bytes (192 bits).
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // Then, we'll generate a random initialization vector
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    // Once we have the key and iv, we can create and use the cipher...
    const cipher = createCipheriv(algorithm, key, iv);

    let encrypted = '';
    cipher.setEncoding('hex');

    cipher.on('data', (chunk) => encrypted += chunk);
    cipher.on('end', () => console.log(encrypted));

    cipher.write('some clear text data');
    cipher.end();
  });
});
javascript

Example: Using Cipheriv and piped streams:

import {
  createReadStream,
  createWriteStream,
} from 'node:fs';

import {
  pipeline,
} from 'node:stream';

const {
  scrypt,
  randomFill,
  createCipheriv,
} = await import('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';

// First, we'll generate the key. The key length is dependent on the algorithm.
// In this case for aes192, it is 24 bytes (192 bits).
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // Then, we'll generate a random initialization vector
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    const cipher = createCipheriv(algorithm, key, iv);

    const input = createReadStream('test.js');
    const output = createWriteStream('test.enc');

    pipeline(input, cipher, output, (err) => {
      if (err) throw err;
    });
  });
});
const {
  createReadStream,
  createWriteStream,
} = require('node:fs');

const {
  pipeline,
} = require('node:stream');

const {
  scrypt,
  randomFill,
  createCipheriv,
} = require('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';

// First, we'll generate the key. The key length is dependent on the algorithm.
// In this case for aes192, it is 24 bytes (192 bits).
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // Then, we'll generate a random initialization vector
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    const cipher = createCipheriv(algorithm, key, iv);

    const input = createReadStream('test.js');
    const output = createWriteStream('test.enc');

    pipeline(input, cipher, output, (err) => {
      if (err) throw err;
    });
  });
});
javascript

Example: Using the cipher.update() and cipher.final() methods:

const {
  scrypt,
  randomFill,
  createCipheriv,
} = await import('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';

// First, we'll generate the key. The key length is dependent on the algorithm.
// In this case for aes192, it is 24 bytes (192 bits).
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // Then, we'll generate a random initialization vector
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    const cipher = createCipheriv(algorithm, key, iv);

    let encrypted = cipher.update('some clear text data', 'utf8', 'hex');
    encrypted += cipher.final('hex');
    console.log(encrypted);
  });
});
const {
  scrypt,
  randomFill,
  createCipheriv,
} = require('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';

// First, we'll generate the key. The key length is dependent on the algorithm.
// In this case for aes192, it is 24 bytes (192 bits).
scrypt(password, 'salt', 24, (err, key) => {
  if (err) throw err;
  // Then, we'll generate a random initialization vector
  randomFill(new Uint8Array(16), (err, iv) => {
    if (err) throw err;

    const cipher = createCipheriv(algorithm, key, iv);

    let encrypted = cipher.update('some clear text data', 'utf8', 'hex');
    encrypted += cipher.final('hex');
    console.log(encrypted);
  });
});
javascript

cipher.final([outputEncoding])#

  • outputEncoding <string> The encoding of the return value.
  • Returns: <Buffer> | <string> Any remaining enciphered contents. If outputEncoding is specified, a string is returned. If an outputEncoding is not provided, a Buffer is returned.

If an output encoding was specified in a previous call to cipher.update(), outputEncoding must use the same encoding.

Once the cipher.final() method has been called, the Cipheriv object can no longer be used to encrypt data. Attempts to call cipher.final() more than once will result in an error being thrown.

cipher.getAuthTag()#

  • Returns: <Buffer> When using an authenticated encryption mode (GCM, CCM, OCB, SIV, GCM-SIV, and chacha20-poly1305 are currently supported), the cipher.getAuthTag() method returns a Buffer containing the authentication tag that has been computed from the given data.

The cipher.getAuthTag() method should only be called after encryption has been completed using the cipher.final() method.

If the authTagLength option was set during the cipher instance's creation, this function will return exactly authTagLength bytes.

cipher.setAAD(buffer[, options])#

When using an authenticated encryption mode (GCM, CCM, OCB, SIV, GCM-SIV, and chacha20-poly1305 are currently supported), the cipher.setAAD() method sets the value used for the additional authenticated data (AAD) input parameter.

The plaintextLength option is optional for GCM, OCB, SIV, and GCM-SIV. When using CCM, the plaintextLength option must be specified and its value must match the length of the plaintext in bytes. See CCM mode.

The cipher.setAAD() method must be called before cipher.update().

cipher.setAutoPadding([autoPadding])#

  • autoPadding <boolean> Default: true
  • Returns: <Cipheriv> The same Cipheriv instance for method chaining.

When using block ciphers that use standard block padding, the Cipheriv class will automatically add padding to the input data to the appropriate block size. To disable the default padding call cipher.setAutoPadding(false).

For block ciphers that use standard block padding, when autoPadding is false, the length of the entire input data must be a multiple of the cipher's block size or cipher.final() will throw an error. Disabling automatic padding is useful for non-standard padding, for instance using 0x0 instead of PKCS padding.

The cipher.setAutoPadding() method must be called before cipher.final().

cipher.update(data[, inputEncoding][, outputEncoding])#

Updates the cipher with data. If the inputEncoding argument is given, the data argument is a string using the specified encoding. If the inputEncoding argument is not given, data must be a Buffer, TypedArray, or DataView. If data is a Buffer, TypedArray, or DataView, then inputEncoding is ignored.

The outputEncoding specifies the output format of the enciphered data. If the outputEncoding is specified, a string using the specified encoding is returned. If no outputEncoding is provided, a Buffer is returned. When outputEncoding is specified, it must use the same encoding as previous calls to cipher.update().

For most algorithms, cipher.update() can be called multiple times with new data until cipher.final() is called. Some algorithms restrict calls to cipher.update(). For example, CCM mode, CBC-CTS mode, XTS mode, AES key wrap modes, and SIV and GCM-SIV modes require the whole message in a single call. Calling cipher.update() after cipher.final() will result in an error being thrown.

Class: Decipheriv#

Instances of the Decipheriv class are used to decrypt data. The class can be used in one of two ways:

  • As a stream that is both readable and writable, where plain encrypted data is written to produce unencrypted data on the readable side, or
  • Using the decipher.update() and decipher.final() methods to produce the unencrypted data.

The crypto.createDecipheriv() method is used to create Decipheriv instances. Decipheriv objects are not to be created directly using the new keyword.

The selected algorithm may impose additional restrictions on streaming and calls to decipher.update(). See CCM mode, CBC-CTS mode, XTS mode, AES key wrap modes, and SIV and GCM-SIV modes.

Example: Using Decipheriv objects as streams:

import { Buffer } from 'node:buffer';
const {
  scryptSync,
  createDecipheriv,
} = await import('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// Key length is dependent on the algorithm. In this case for aes192, it is
// 24 bytes (192 bits).
// Use the async `crypto.scrypt()` instead.
const key = scryptSync(password, 'salt', 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.

const decipher = createDecipheriv(algorithm, key, iv);

let decrypted = '';
decipher.on('readable', () => {
  let chunk;
  while (null !== (chunk = decipher.read())) {
    decrypted += chunk.toString('utf8');
  }
});
decipher.on('end', () => {
  console.log(decrypted);
  // Prints: some clear text data
});

// Encrypted with same algorithm, key and iv.
const encrypted =
  'e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa';
decipher.write(encrypted, 'hex');
decipher.end();
const {
  scryptSync,
  createDecipheriv,
} = require('node:crypto');
const { Buffer } = require('node:buffer');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// Key length is dependent on the algorithm. In this case for aes192, it is
// 24 bytes (192 bits).
// Use the async `crypto.scrypt()` instead.
const key = scryptSync(password, 'salt', 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.

const decipher = createDecipheriv(algorithm, key, iv);

let decrypted = '';
decipher.on('readable', () => {
  let chunk;
  while (null !== (chunk = decipher.read())) {
    decrypted += chunk.toString('utf8');
  }
});
decipher.on('end', () => {
  console.log(decrypted);
  // Prints: some clear text data
});

// Encrypted with same algorithm, key and iv.
const encrypted =
  'e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa';
decipher.write(encrypted, 'hex');
decipher.end();
javascript

Example: Using Decipheriv and piped streams:

import {
  createReadStream,
  createWriteStream,
} from 'node:fs';
import { Buffer } from 'node:buffer';
const {
  scryptSync,
  createDecipheriv,
} = await import('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// Use the async `crypto.scrypt()` instead.
const key = scryptSync(password, 'salt', 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.

const decipher = createDecipheriv(algorithm, key, iv);

const input = createReadStream('test.enc');
const output = createWriteStream('test.js');

input.pipe(decipher).pipe(output);
const {
  createReadStream,
  createWriteStream,
} = require('node:fs');
const {
  scryptSync,
  createDecipheriv,
} = require('node:crypto');
const { Buffer } = require('node:buffer');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// Use the async `crypto.scrypt()` instead.
const key = scryptSync(password, 'salt', 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.

const decipher = createDecipheriv(algorithm, key, iv);

const input = createReadStream('test.enc');
const output = createWriteStream('test.js');

input.pipe(decipher).pipe(output);
javascript

Example: Using the decipher.update() and decipher.final() methods:

import { Buffer } from 'node:buffer';
const {
  scryptSync,
  createDecipheriv,
} = await import('node:crypto');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// Use the async `crypto.scrypt()` instead.
const key = scryptSync(password, 'salt', 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.

const decipher = createDecipheriv(algorithm, key, iv);

// Encrypted using same algorithm, key and iv.
const encrypted =
  'e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa';
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
// Prints: some clear text data
const {
  scryptSync,
  createDecipheriv,
} = require('node:crypto');
const { Buffer } = require('node:buffer');

const algorithm = 'aes-192-cbc';
const password = 'Password used to generate key';
// Use the async `crypto.scrypt()` instead.
const key = scryptSync(password, 'salt', 24);
// The IV is usually passed along with the ciphertext.
const iv = Buffer.alloc(16, 0); // Initialization vector.

const decipher = createDecipheriv(algorithm, key, iv);

// Encrypted using same algorithm, key and iv.
const encrypted =
  'e5f79c5915c02171eec6b212d5520d44480993d7d622a7c4c2da32f6efda0ffa';
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
console.log(decrypted);
// Prints: some clear text data
javascript

decipher.final([outputEncoding])#

  • outputEncoding <string> The encoding of the return value.
  • Returns: <Buffer> | <string> Any remaining deciphered contents. If outputEncoding is specified, a string is returned. If an outputEncoding is not provided, a Buffer is returned.

If an output encoding was specified in a previous call to decipher.update(), outputEncoding must use the same encoding.

Once the decipher.final() method has been called, the Decipheriv object can no longer be used to decrypt data. Attempts to call decipher.final() more than once will result in an error being thrown.

decipher.setAAD(buffer[, options])#

When using an authenticated encryption mode (GCM, CCM, OCB, SIV, GCM-SIV, and chacha20-poly1305 are currently supported), the decipher.setAAD() method sets the value used for the additional authenticated data (AAD) input parameter.

The options argument is optional for GCM, OCB, SIV, and GCM-SIV. When using CCM, the plaintextLength option must be specified and its value must match the length of the ciphertext in bytes. See CCM mode.

The decipher.setAAD() method must be called before decipher.update().

When passing a string as the buffer, please consider caveats when using strings as inputs to cryptographic APIs.

decipher.setAuthTag(buffer[, encoding])#

When using an authenticated encryption mode (GCM, CCM, OCB, SIV, GCM-SIV, and chacha20-poly1305 are currently supported), the decipher.setAuthTag() method is used to pass in the received authentication tag. If no tag is provided, or if the cipher text has been tampered with, decipher.final() will throw, indicating that the cipher text should be discarded due to failed authentication. If the tag length is invalid according to NIST SP 800-38D or does not match the value of the authTagLength option, decipher.setAuthTag() will throw an error.

The decipher.setAuthTag() method must be called before decipher.update() for CCM, SIV, and GCM-SIV modes or before decipher.final() for GCM and OCB modes and chacha20-poly1305. decipher.setAuthTag() can only be called once.

When passing a string as the authentication tag, please consider caveats when using strings as inputs to cryptographic APIs.

decipher.setAutoPadding([autoPadding])#

  • autoPadding <boolean> Default: true
  • Returns: <Decipheriv> The same Decipheriv instance for method chaining.

When data has been encrypted without standard block padding, calling decipher.setAutoPadding(false) will disable automatic padding to prevent decipher.final() from checking for and removing padding.

For block ciphers that use standard block padding, disabling it requires the input data's length to be a multiple of the cipher's block size.

The decipher.setAutoPadding() method must be called before decipher.final().

decipher.update(data[, inputEncoding][, outputEncoding])#

Updates the decipher with data. If the inputEncoding argument is given, the data argument is a string using the specified encoding. If the inputEncoding argument is not given, data must be a Buffer, TypedArray, or DataView. If data is a Buffer, TypedArray, or DataView, then inputEncoding is ignored.

The outputEncoding specifies the output format of the deciphered data. If the outputEncoding is specified, a string using the specified encoding is returned. If no outputEncoding is provided, a Buffer is returned. When outputEncoding is specified, it must use the same encoding as previous calls to decipher.update().

For most algorithms, decipher.update() can be called multiple times with new data until decipher.final() is called. Some algorithms restrict calls to decipher.update(). For example, CCM mode, CBC-CTS mode, XTS mode, AES key wrap modes, and SIV and GCM-SIV modes require the whole message in a single call. Calling decipher.update() after decipher.final() will result in an error being thrown.

Even if the underlying cipher implements authentication, the authenticity and integrity of the plaintext returned from this function may be uncertain at this time. For authenticated encryption algorithms, authenticity is generally only established when the application calls decipher.final().

Class: DiffieHellman#

The DiffieHellman class is a utility for creating Diffie-Hellman key exchanges.

Instances of the DiffieHellman class can be created using the crypto.createDiffieHellman() function.

import assert from 'node:assert';

const {
  createDiffieHellman,
} = await import('node:crypto');

// Generate Alice's keys...
const alice = createDiffieHellman(2048);
const aliceKey = alice.generateKeys();

// Generate Bob's keys...
const bob = createDiffieHellman(alice.getPrime(), alice.getGenerator());
const bobKey = bob.generateKeys();

// Exchange and generate the secret...
const aliceSecret = alice.computeSecret(bobKey);
const bobSecret = bob.computeSecret(aliceKey);

// OK
assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex'));
const assert = require('node:assert');

const {
  createDiffieHellman,
} = require('node:crypto');

// Generate Alice's keys...
const alice = createDiffieHellman(2048);
const aliceKey = alice.generateKeys();

// Generate Bob's keys...
const bob = createDiffieHellman(alice.getPrime(), alice.getGenerator());
const bobKey = bob.generateKeys();

// Exchange and generate the secret...
const aliceSecret = alice.computeSecret(bobKey);
const bobSecret = bob.computeSecret(aliceKey);

// OK
assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex'));
javascript

diffieHellman.computeSecret(otherPublicKey[, inputEncoding][, outputEncoding])#

Computes the shared secret using otherPublicKey as the other party's public key and returns the computed shared secret. The supplied key is interpreted using the specified inputEncoding, and secret is encoded using specified outputEncoding. If the inputEncoding is not provided, otherPublicKey is expected to be a Buffer, TypedArray, or DataView.

If outputEncoding is given a string is returned; otherwise, a Buffer is returned.

diffieHellman.generateKeys([encoding])#

Generates private and public Diffie-Hellman key values unless they have been generated or computed already, and returns the public key in the specified encoding. This key should be transferred to the other party. If encoding is provided a string is returned; otherwise a Buffer is returned.

This function is a thin wrapper around DH_generate_key(). In particular, once a private key has been generated or set, calling this function only recomputes the public key from the existing private key. Since the public key is determined by the private key, the result will be the same unless the private key has been changed via diffieHellman.setPrivateKey().

diffieHellman.getGenerator([encoding])#

Returns the Diffie-Hellman generator in the specified encoding. If encoding is provided a string is returned; otherwise a Buffer is returned.

diffieHellman.getPrime([encoding])#

Returns the Diffie-Hellman prime in the specified encoding. If encoding is provided a string is returned; otherwise a Buffer is returned.

diffieHellman.getPrivateKey([encoding])#

Returns the Diffie-Hellman private key in the specified encoding. If encoding is provided a string is returned; otherwise a Buffer is returned.

diffieHellman.getPublicKey([encoding])#

Returns the Diffie-Hellman public key in the specified encoding. If encoding is provided a string is returned; otherwise a Buffer is returned.

diffieHellman.setPrivateKey(privateKey[, encoding])#

Sets the Diffie-Hellman private key. If the encoding argument is provided, privateKey is expected to be a string. If no encoding is provided, privateKey is expected to be a Buffer, TypedArray, or DataView.

This function does not automatically compute the associated public key. Either diffieHellman.setPublicKey() or diffieHellman.generateKeys() can be used to manually provide the public key or to automatically derive it.

diffieHellman.setPublicKey(publicKey[, encoding])#

Sets the Diffie-Hellman public key. If the encoding argument is provided, publicKey is expected to be a string. If no encoding is provided, publicKey is expected to be a Buffer, TypedArray, or DataView.

diffieHellman.verifyError#

A bit field containing any warnings and/or errors resulting from a check performed during initialization of the DiffieHellman object.

The following values are valid for this property (as defined in node:constants module):

  • DH_CHECK_P_NOT_SAFE_PRIME
  • DH_CHECK_P_NOT_PRIME
  • DH_UNABLE_TO_CHECK_GENERATOR
  • DH_NOT_SUITABLE_GENERATOR

Class: DiffieHellmanGroup#

The DiffieHellmanGroup class takes a well-known modp group as its argument. It works the same as DiffieHellman, except that it does not allow changing its keys after creation. In other words, it does not implement setPublicKey() or setPrivateKey() methods.

const { createDiffieHellmanGroup } = await import('node:crypto');
const dh = createDiffieHellmanGroup('modp16');
const { createDiffieHellmanGroup } = require('node:crypto');
const dh = createDiffieHellmanGroup('modp16');
javascript

The following groups are supported:

  • 'modp14' (2048 bits, RFC 3526 Section 3)
  • 'modp15' (3072 bits, RFC 3526 Section 4)
  • 'modp16' (4096 bits, RFC 3526 Section 5)
  • 'modp17' (6144 bits, RFC 3526 Section 6)
  • 'modp18' (8192 bits, RFC 3526 Section 7)

The following groups are still supported but deprecated (see Caveats):

  • 'modp1' (768 bits, RFC 2409 Section 6.1)
  • 'modp2' (1024 bits, RFC 2409 Section 6.2)
  • 'modp5' (1536 bits, RFC 3526 Section 2)

These deprecated groups might be removed in future versions of Node.js.

Class: ECDH#

The ECDH class is a utility for creating Elliptic Curve Diffie-Hellman (ECDH) key exchanges.

Instances of the ECDH class can be created using the crypto.createECDH() function.

import assert from 'node:assert';

const {
  createECDH,
} = await import('node:crypto');

// Generate Alice's keys...
const alice = createECDH('secp521r1');
const aliceKey = alice.generateKeys();

// Generate Bob's keys...
const bob = createECDH('secp521r1');
const bobKey = bob.generateKeys();

// Exchange and generate the secret...
const aliceSecret = alice.computeSecret(bobKey);
const bobSecret = bob.computeSecret(aliceKey);

assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex'));
// OK
const assert = require('node:assert');

const {
  createECDH,
} = require('node:crypto');

// Generate Alice's keys...
const alice = createECDH('secp521r1');
const aliceKey = alice.generateKeys();

// Generate Bob's keys...
const bob = createECDH('secp521r1');
const bobKey = bob.generateKeys();

// Exchange and generate the secret...
const aliceSecret = alice.computeSecret(bobKey);
const bobSecret = bob.computeSecret(aliceKey);

assert.strictEqual(aliceSecret.toString('hex'), bobSecret.toString('hex'));
// OK
javascript

Static method: ECDH.convertKey(key, curve[, inputEncoding[, outputEncoding[, format]]])#

Converts the EC Diffie-Hellman public key specified by key and curve to the format specified by format. The format argument specifies point encoding and can be 'compressed', 'uncompressed' or 'hybrid'. The supplied key is interpreted using the specified inputEncoding, and the returned key is encoded using the specified outputEncoding.

Use crypto.getCurves() to obtain a list of available curve names. On recent OpenSSL releases, openssl ecparam -list_curves will also display the name and description of each available elliptic curve.

If format is not specified the point will be returned in 'uncompressed' format.

If the inputEncoding is not provided, key is expected to be a Buffer, TypedArray, or DataView.

Example (uncompressing a key):

const {
  createECDH,
  ECDH,
} = await import('node:crypto');

const ecdh = createECDH('secp256k1');
ecdh.generateKeys();

const compressedKey = ecdh.getPublicKey('hex', 'compressed');

const uncompressedKey = ECDH.convertKey(compressedKey,
                                        'secp256k1',
                                        'hex',
                                        'hex',
                                        'uncompressed');

// The converted key and the uncompressed public key should be the same
console.log(uncompressedKey === ecdh.getPublicKey('hex'));
const {
  createECDH,
  ECDH,
} = require('node:crypto');

const ecdh = createECDH('secp256k1');
ecdh.generateKeys();

const compressedKey = ecdh.getPublicKey('hex', 'compressed');

const uncompressedKey = ECDH.convertKey(compressedKey,
                                        'secp256k1',
                                        'hex',
                                        'hex',
                                        'uncompressed');

// The converted key and the uncompressed public key should be the same
console.log(uncompressedKey === ecdh.getPublicKey('hex'));
javascript

ecdh.computeSecret(otherPublicKey[, inputEncoding][, outputEncoding])#

Computes the shared secret using otherPublicKey as the other party's public key and returns the computed shared secret. The supplied key is interpreted using specified inputEncoding, and the returned secret is encoded using the specified outputEncoding. If the inputEncoding is not provided, otherPublicKey is expected to be a Buffer, TypedArray, or DataView.

If outputEncoding is given a string will be returned; otherwise a Buffer is returned.

ecdh.computeSecret will throw an ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY error when otherPublicKey lies outside of the elliptic curve. Since otherPublicKey is usually supplied from a remote user over an insecure network, be sure to handle this exception accordingly.

ecdh.generateKeys([encoding[, format]])#

Generates private and public EC Diffie-Hellman key values, and returns the public key in the specified format and encoding. This key should be transferred to the other party.

The format argument specifies point encoding and can be 'compressed' or 'uncompressed'. If format is not specified, the point will be returned in 'uncompressed' format.

If encoding is provided a string is returned; otherwise a Buffer is returned.

ecdh.getPrivateKey([encoding])#

If encoding is specified, a string is returned; otherwise a Buffer is returned.

ecdh.getPublicKey([encoding][, format])#

The format argument specifies point encoding and can be 'compressed' or 'uncompressed'. If format is not specified the point will be returned in 'uncompressed' format.

If encoding is specified, a string is returned; otherwise a Buffer is returned.

ecdh.setPrivateKey(privateKey[, encoding])#

Sets the EC Diffie-Hellman private key. If encoding is provided, privateKey is expected to be a string; otherwise privateKey is expected to be a Buffer, TypedArray, or DataView.

If privateKey is not valid for the curve specified when the ECDH object was created, an error is thrown. Upon setting the private key, the associated public point (key) is also generated and set in the ECDH object.

ecdh.setPublicKey(publicKey[, encoding])#

Stability: 0 - Deprecated

Sets the EC Diffie-Hellman public key. If encoding is provided publicKey is expected to be a string; otherwise a Buffer, TypedArray, or DataView is expected.

There is not normally a reason to call this method because ECDH only requires a private key and the other party's public key to compute the shared secret. Typically either ecdh.generateKeys() or ecdh.setPrivateKey() will be called. The ecdh.setPrivateKey() method attempts to generate the public point/key associated with the private key being set.

Example (obtaining a shared secret):

const {
  createECDH,
  createHash,
} = await import('node:crypto');

const alice = createECDH('secp256k1');
const bob = createECDH('secp256k1');

// This is a shortcut way of specifying one of Alice's previous private
// keys. It would be unwise to use such a predictable private key in a real
// application.
alice.setPrivateKey(
  createHash('sha256').update('alice', 'utf8').digest(),
);

// Bob uses a newly generated cryptographically strong
// pseudorandom key pair
bob.generateKeys();

const aliceSecret = alice.computeSecret(bob.getPublicKey(), null, 'hex');
const bobSecret = bob.computeSecret(alice.getPublicKey(), null, 'hex');

// aliceSecret and bobSecret should be the same shared secret value
console.log(aliceSecret === bobSecret);
const {
  createECDH,
  createHash,
} = require('node:crypto');

const alice = createECDH('secp256k1');
const bob = createECDH('secp256k1');

// This is a shortcut way of specifying one of Alice's previous private
// keys. It would be unwise to use such a predictable private key in a real
// application.
alice.setPrivateKey(
  createHash('sha256').update('alice', 'utf8').digest(),
);

// Bob uses a newly generated cryptographically strong
// pseudorandom key pair
bob.generateKeys();

const aliceSecret = alice.computeSecret(bob.getPublicKey(), null, 'hex');
const bobSecret = bob.computeSecret(alice.getPublicKey(), null, 'hex');

// aliceSecret and bobSecret should be the same shared secret value
console.log(aliceSecret === bobSecret);
javascript

Class: Hash#

The Hash class is a utility for creating hash digests of data. It can be used in one of two ways:

  • As a stream that is both readable and writable, where data is written to produce a computed hash digest on the readable side, or
  • Using the hash.update() and hash.digest() methods to produce the computed hash.

The crypto.createHash() method is used to create Hash instances. Hash objects are not to be created directly using the new keyword.

Example: Using Hash objects as streams:

const {
  createHash,
} = await import('node:crypto');

const hash = createHash('sha256');

hash.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = hash.read();
  if (data) {
    console.log(data.toString('hex'));
    // Prints:
    //   6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50
  }
});

hash.write('some data to hash');
hash.end();
const {
  createHash,
} = require('node:crypto');

const hash = createHash('sha256');

hash.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = hash.read();
  if (data) {
    console.log(data.toString('hex'));
    // Prints:
    //   6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50
  }
});

hash.write('some data to hash');
hash.end();
javascript

Example: Using Hash and piped streams:

import { createReadStream } from 'node:fs';
import { stdout } from 'node:process';
const { createHash } = await import('node:crypto');

const hash = createHash('sha256');

const input = createReadStream('test.js');
input.pipe(hash).setEncoding('hex').pipe(stdout);
const { createReadStream } = require('node:fs');
const { createHash } = require('node:crypto');
const { stdout } = require('node:process');

const hash = createHash('sha256');

const input = createReadStream('test.js');
input.pipe(hash).setEncoding('hex').pipe(stdout);
javascript

Example: Using the hash.update() and hash.digest() methods:

const {
  createHash,
} = await import('node:crypto');

const hash = createHash('sha256');

hash.update('some data to hash');
console.log(hash.digest('hex'));
// Prints:
//   6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50
const {
  createHash,
} = require('node:crypto');

const hash = createHash('sha256');

hash.update('some data to hash');
console.log(hash.digest('hex'));
// Prints:
//   6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50
javascript

hash.copy([options])#

Creates a new Hash object that contains a deep copy of the internal state of the current Hash object.

The optional options argument controls stream behavior. For XOF hash functions such as 'shake256', the outputLength option can be used to specify the desired output length in bytes.

An error is thrown when an attempt is made to copy the Hash object after its hash.digest() method has been called.

// Calculate a rolling hash.
const {
  createHash,
} = await import('node:crypto');

const hash = createHash('sha256');

hash.update('one');
console.log(hash.copy().digest('hex'));

hash.update('two');
console.log(hash.copy().digest('hex'));

hash.update('three');
console.log(hash.copy().digest('hex'));

// Etc.
// Calculate a rolling hash.
const {
  createHash,
} = require('node:crypto');

const hash = createHash('sha256');

hash.update('one');
console.log(hash.copy().digest('hex'));

hash.update('two');
console.log(hash.copy().digest('hex'));

hash.update('three');
console.log(hash.copy().digest('hex'));

// Etc.
javascript

hash.digest([encoding])#

Calculates the digest of all of the data passed to be hashed (using the hash.update() method). If encoding is provided a string will be returned; otherwise a Buffer is returned.

The Hash object can not be used again after hash.digest() method has been called. Multiple calls will cause an error to be thrown.

hash.update(data[, inputEncoding])#

Updates the hash content with the given data, the encoding of which is given in inputEncoding. If encoding is not provided, and the data is a string, an encoding of 'utf8' is enforced. If data is a Buffer, TypedArray, or DataView, then inputEncoding is ignored.

This can be called many times with new data as it is streamed.

Class: Hmac#

The Hmac class is a utility for creating cryptographic HMAC digests. It can be used in one of two ways:

  • As a stream that is both readable and writable, where data is written to produce a computed HMAC digest on the readable side, or
  • Using the hmac.update() and hmac.digest() methods to produce the computed HMAC digest.

The crypto.createHmac() method is used to create Hmac instances. Hmac objects are not to be created directly using the new keyword.

Example: Using Hmac objects as streams:

const {
  createHmac,
} = await import('node:crypto');

const hmac = createHmac('sha256', 'a secret');

hmac.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = hmac.read();
  if (data) {
    console.log(data.toString('hex'));
    // Prints:
    //   7fd04df92f636fd450bc841c9418e5825c17f33ad9c87c518115a45971f7f77e
  }
});

hmac.write('some data to hash');
hmac.end();
const {
  createHmac,
} = require('node:crypto');

const hmac = createHmac('sha256', 'a secret');

hmac.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = hmac.read();
  if (data) {
    console.log(data.toString('hex'));
    // Prints:
    //   7fd04df92f636fd450bc841c9418e5825c17f33ad9c87c518115a45971f7f77e
  }
});

hmac.write('some data to hash');
hmac.end();
javascript

Example: Using Hmac and piped streams:

import { createReadStream } from 'node:fs';
import { stdout } from 'node:process';
const {
  createHmac,
} = await import('node:crypto');

const hmac = createHmac('sha256', 'a secret');

const input = createReadStream('test.js');
input.pipe(hmac).pipe(stdout);
const {
  createReadStream,
} = require('node:fs');
const {
  createHmac,
} = require('node:crypto');
const { stdout } = require('node:process');

const hmac = createHmac('sha256', 'a secret');

const input = createReadStream('test.js');
input.pipe(hmac).pipe(stdout);
javascript

Example: Using the hmac.update() and hmac.digest() methods:

const {
  createHmac,
} = await import('node:crypto');

const hmac = createHmac('sha256', 'a secret');

hmac.update('some data to hash');
console.log(hmac.digest('hex'));
// Prints:
//   7fd04df92f636fd450bc841c9418e5825c17f33ad9c87c518115a45971f7f77e
const {
  createHmac,
} = require('node:crypto');

const hmac = createHmac('sha256', 'a secret');

hmac.update('some data to hash');
console.log(hmac.digest('hex'));
// Prints:
//   7fd04df92f636fd450bc841c9418e5825c17f33ad9c87c518115a45971f7f77e
javascript

hmac.digest([encoding])#

Calculates the HMAC digest of all of the data passed using hmac.update(). If encoding is provided a string is returned; otherwise a Buffer is returned;

The Hmac object can not be used again after hmac.digest() has been called. Multiple calls to hmac.digest() will result in an error being thrown.

hmac.update(data[, inputEncoding])#

Updates the Hmac content with the given data, the encoding of which is given in inputEncoding. If encoding is not provided, and the data is a string, an encoding of 'utf8' is enforced. If data is a Buffer, TypedArray, or DataView, then inputEncoding is ignored.

This can be called many times with new data as it is streamed.

Class: KeyObject#

Node.js uses a KeyObject class to represent a symmetric or asymmetric key, and each kind of key exposes different functions. The crypto.createSecretKey(), crypto.createPublicKey() and crypto.createPrivateKey() methods are used to create KeyObject instances. KeyObject objects are not to be created directly using the new keyword.

Most applications should consider using the new KeyObject API instead of passing keys as strings or Buffers due to improved security features.

KeyObject instances can be passed to other threads via postMessage(). The receiver obtains a cloned KeyObject, and the KeyObject does not need to be listed in the transferList argument.

Static method: KeyObject.from(key)#

Returns the underlying <KeyObject> of a <CryptoKey>. The returned <KeyObject> does not retain any of the restrictions imposed by the Web Crypto API on the original <CryptoKey>, such as the allowed key usages, the algorithm or hash algorithm bindings, and the extractability flag. In particular, the underlying key material of the returned <KeyObject> can always be exported.

const { KeyObject } = await import('node:crypto');
const { subtle } = globalThis.crypto;

const key = await subtle.generateKey({
  name: 'HMAC',
  hash: 'SHA-256',
  length: 256,
}, true, ['sign', 'verify']);

const keyObject = KeyObject.from(key);
console.log(keyObject.symmetricKeySize);
// Prints: 32 (symmetric key size in bytes)
const { KeyObject } = require('node:crypto');
const { subtle } = globalThis.crypto;

(async function() {
  const key = await subtle.generateKey({
    name: 'HMAC',
    hash: 'SHA-256',
    length: 256,
  }, true, ['sign', 'verify']);

  const keyObject = KeyObject.from(key);
  console.log(keyObject.symmetricKeySize);
  // Prints: 32 (symmetric key size in bytes)
})();
javascript

keyObject.asymmetricKeyDetails#

  • Type: <Object>
    • modulusLength <number> Key size in bits (RSA, DSA).
    • publicExponent <bigint> Public exponent (RSA).
    • hashAlgorithm <string> Name of the message digest (RSA-PSS).
    • mgf1HashAlgorithm <string> Name of the message digest used by MGF1 (RSA-PSS).
    • saltLength <number> Minimal salt length in bytes (RSA-PSS).
    • divisorLength <number> Size of q in bits (DSA).
    • namedCurve <string> Name of the curve (EC).

This property exists only on asymmetric keys. Depending on the type of the key, this object contains information about the key. None of the information obtained through this property can be used to uniquely identify a key or to compromise the security of the key.

For RSA-PSS keys, if the key material contains a RSASSA-PSS-params sequence, the hashAlgorithm, mgf1HashAlgorithm, and saltLength properties will be set.

Other key details might be exposed via this API using additional attributes.

keyObject.asymmetricKeyType#

For asymmetric keys, this property represents the type of the key. See the supported asymmetric key types.

This property is undefined for unrecognized KeyObject types and symmetric keys.

keyObject.equals(otherKeyObject)#

Returns true or false depending on whether the keys have exactly the same type, value, and parameters. This method is not constant time.

keyObject.export([options])#

For symmetric keys, the following encoding options can be used:

  • format <string> Must be 'buffer' (default) or 'jwk'.

For public keys, the following encoding options can be used:

  • format <string> Must be 'pem', 'der', 'jwk', or 'raw-public'. See asymmetric key types for format support.
  • type <string> When format is 'pem' or 'der', must be 'pkcs1' (RSA only) or 'spki'. For EC keys with 'raw-public' format, may be 'uncompressed' (default) or 'compressed'. Ignored when format is 'jwk'.

For private keys, the following encoding options can be used:

  • format <string> Must be 'pem', 'der', 'jwk', 'raw-private', or 'raw-seed'. See asymmetric key types for format support.
  • type <string> When format is 'pem' or 'der', must be 'pkcs1' (RSA only), 'pkcs8', or 'sec1' (EC only). Ignored when format is 'jwk', 'raw-private', or 'raw-seed'.
  • cipher <string> If specified, the private key will be encrypted with the given cipher and passphrase using PKCS#5 v2.0 password based encryption. Ignored when format is 'jwk', 'raw-private', or 'raw-seed'.
  • passphrase <string> | <Buffer> The passphrase to use for encryption. Required when cipher is specified.

The result type depends on the selected encoding format, when PEM the result is a string, when DER it will be a buffer containing the data encoded as DER, when JWK it will be an object. Raw formats return a <Buffer> containing the raw key material.

Private keys can be encrypted by specifying a cipher and passphrase. The PKCS#8 type supports encryption with both PEM and DER format for any key algorithm. PKCS#1 and SEC1 can only be encrypted when the PEM format is used. For maximum compatibility, use PKCS#8 for encrypted private keys. Since PKCS#8 defines its own encryption mechanism, PEM-level encryption is not supported when encrypting a PKCS#8 key. See RFC 5208 for PKCS#8 encryption and RFC 1421 for PKCS#1 and SEC1 encryption.

keyObject.symmetricKeySize#

For secret keys, this property represents the size of the key in bytes. This property is undefined for asymmetric keys.

keyObject.toCryptoKey(algorithm, extractable, keyUsages)#

Converts a KeyObject instance to a CryptoKey.

keyObject.type#

Depending on the type of this KeyObject, this property is either 'secret' for secret (symmetric) keys, 'public' for public (asymmetric) keys or 'private' for private (asymmetric) keys.

Class: Mac#

The Mac class computes message authentication codes using MAC implementations supplied by OpenSSL providers. It can be used in one of two ways:

  • As a stream that is both readable and writable, where data is written and one authentication tag is produced on the readable side when the writable side ends; or
  • By calling mac.update() one or more times followed by mac.final().

Instances of Mac are created using crypto.createMac(). The Mac class is not exported directly by the node:crypto module.

Calling mac.end() without first writing data computes the authentication tag for an empty message. If the selected MAC produces a zero-byte tag, such as when a provider accepts outputLength: 0, the readable side ends without emitting a data chunk because Node.js streams do not emit zero-length chunks. When using mac.final() instead, it returns a zero-length Buffer or an empty encoded string.

mac.end() and mac.final() are alternative terminal operations and must not both be called on the same object. A Mac object cannot be used again after either operation attempts finalization or after an underlying MAC update fails.

Example: Using mac.update() and mac.final():

const { createMac, randomBytes } = await import('node:crypto');

const key = randomBytes(16);
const mac = createMac('CMAC', key, {
  cipher: 'AES-128-CBC',
});

mac.update('some data to authenticate');
console.log(mac.final('hex'));
mjs

mac.final([outputEncoding])#

Completes the MAC computation and returns the authentication tag. If outputEncoding is omitted or is 'buffer', a Buffer is returned. Otherwise, a string is returned.

To verify an authentication tag, compare equal-length Buffer values using crypto.timingSafeEqual().

The Mac object cannot be used again after finalization is attempted, including when finalization fails. Later calls to mac.update() or mac.final() throw ERR_CRYPTO_MAC_FINALIZED.

mac.update(data[, inputEncoding])#

Updates the MAC with data and returns the Mac object so that calls can be chained. When data is a string, inputEncoding defaults to 'utf8'. When data is a Buffer, TypedArray, or DataView, inputEncoding is ignored.

This method can be called multiple times before finalization. If an underlying MAC update fails, the Mac object cannot be used again. Calling this method after a previous underlying MAC update failure or after finalization throws ERR_CRYPTO_MAC_FINALIZED.

Class: Sign#

The Sign class is a utility for generating signatures. It can be used in one of two ways:

  • As a writable stream, where data to be signed is written and the sign.sign() method is used to generate and return the signature, or
  • Using the sign.update() and sign.sign() methods to produce the signature.

The crypto.createSign() method is used to create Sign instances. The argument is the string name of the hash function to use. Sign objects are not to be created directly using the new keyword.

Example: Using Sign and Verify objects as streams:

const {
  generateKeyPairSync,
  createSign,
  createVerify,
} = await import('node:crypto');

const { privateKey, publicKey } = generateKeyPairSync('ec', {
  namedCurve: 'sect239k1',
});

const sign = createSign('SHA256');
sign.write('some data to sign');
sign.end();
const signature = sign.sign(privateKey, 'hex');

const verify = createVerify('SHA256');
verify.write('some data to sign');
verify.end();
console.log(verify.verify(publicKey, signature, 'hex'));
// Prints: true
const {
  generateKeyPairSync,
  createSign,
  createVerify,
} = require('node:crypto');

const { privateKey, publicKey } = generateKeyPairSync('ec', {
  namedCurve: 'sect239k1',
});

const sign = createSign('SHA256');
sign.write('some data to sign');
sign.end();
const signature = sign.sign(privateKey, 'hex');

const verify = createVerify('SHA256');
verify.write('some data to sign');
verify.end();
console.log(verify.verify(publicKey, signature, 'hex'));
// Prints: true
javascript

Example: Using the sign.update() and verify.update() methods:

const {
  generateKeyPairSync,
  createSign,
  createVerify,
} = await import('node:crypto');

const { privateKey, publicKey } = generateKeyPairSync('rsa', {
  modulusLength: 2048,
});

const sign = createSign('SHA256');
sign.update('some data to sign');
sign.end();
const signature = sign.sign(privateKey);

const verify = createVerify('SHA256');
verify.update('some data to sign');
verify.end();
console.log(verify.verify(publicKey, signature));
// Prints: true
const {
  generateKeyPairSync,
  createSign,
  createVerify,
} = require('node:crypto');

const { privateKey, publicKey } = generateKeyPairSync('rsa', {
  modulusLength: 2048,
});

const sign = createSign('SHA256');
sign.update('some data to sign');
sign.end();
const signature = sign.sign(privateKey);

const verify = createVerify('SHA256');
verify.update('some data to sign');
verify.end();
console.log(verify.verify(publicKey, signature));
// Prints: true
javascript

sign.sign(privateKey[, outputEncoding])#

Calculates the signature on all the data passed through using either sign.update() or sign.write().

If privateKey is not a KeyObject, this function behaves as if privateKey had been passed to crypto.createPrivateKey(). When privateKey is a string, ArrayBuffer, Buffer, TypedArray, or DataView, it must contain PEM-encoded key material. If it is an object, the following additional properties can be passed:

  • dsaEncoding <string> For DSA and ECDSA, this option specifies the format of the generated signature. It can be one of the following:

    • 'der' (default): DER-encoded ASN.1 signature structure encoding (r, s).
    • 'ieee-p1363': Signature format r || s as proposed in IEEE-P1363.
  • padding <integer> Optional padding value for RSA, one of the following:

    • crypto.constants.RSA_PKCS1_PADDING (default)
    • crypto.constants.RSA_PKCS1_PSS_PADDING

    RSA_PKCS1_PSS_PADDING will use MGF1 with the same hash function used to sign the message as specified in section 3.1 of RFC 4055, unless an MGF1 hash function has been specified as part of the key in compliance with section 3.3 of RFC 4055.

  • saltLength <integer> Salt length for when padding is RSA_PKCS1_PSS_PADDING. The special value crypto.constants.RSA_PSS_SALTLEN_DIGEST sets the salt length to the digest size, crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN (default) sets it to the maximum permissible value.

If outputEncoding is provided a string is returned; otherwise a Buffer is returned.

The Sign object can not be again used after sign.sign() method has been called. Multiple calls to sign.sign() will result in an error being thrown.

sign.update(data[, inputEncoding])#

Updates the Sign content with the given data, the encoding of which is given in inputEncoding. If encoding is not provided, and the data is a string, an encoding of 'utf8' is enforced. If data is a Buffer, TypedArray, or DataView, then inputEncoding is ignored.

This can be called many times with new data as it is streamed.

Class: Verify#

The Verify class is a utility for verifying signatures. It can be used in one of two ways:

The crypto.createVerify() method is used to create Verify instances. Verify objects are not to be created directly using the new keyword.

See Sign for examples.

verify.update(data[, inputEncoding])#

Updates the Verify content with the given data, the encoding of which is given in inputEncoding. If inputEncoding is not provided, and the data is a string, an encoding of 'utf8' is enforced. If data is a Buffer, TypedArray, or DataView, then inputEncoding is ignored.

This can be called many times with new data as it is streamed.

verify.verify(key, signature[, signatureEncoding])#

Verifies the provided data using the given key and signature.

If key is not a KeyObject, this function behaves as if key had been passed to crypto.createPublicKey(). When key is a string, ArrayBuffer, Buffer, TypedArray, or DataView, it must contain PEM-encoded key material. If it is an object, the following additional properties can be passed:

  • dsaEncoding <string> For DSA and ECDSA, this option specifies the format of the signature. It can be one of the following:

    • 'der' (default): DER-encoded ASN.1 signature structure encoding (r, s).
    • 'ieee-p1363': Signature format r || s as proposed in IEEE-P1363.
  • padding <integer> Optional padding value for RSA, one of the following:

    • crypto.constants.RSA_PKCS1_PADDING (default)
    • crypto.constants.RSA_PKCS1_PSS_PADDING

    RSA_PKCS1_PSS_PADDING will use MGF1 with the same hash function used to verify the message as specified in section 3.1 of RFC 4055, unless an MGF1 hash function has been specified as part of the key in compliance with section 3.3 of RFC 4055.

  • saltLength <integer> Salt length for when padding is RSA_PKCS1_PSS_PADDING. The special value crypto.constants.RSA_PSS_SALTLEN_DIGEST sets the salt length to the digest size, crypto.constants.RSA_PSS_SALTLEN_AUTO (default) causes it to be determined automatically.

The signature argument is the previously calculated signature for the data, in the signatureEncoding. If a signatureEncoding is specified, the signature is expected to be a string; otherwise signature is expected to be a Buffer, TypedArray, or DataView.

The verify object can not be used again after verify.verify() has been called. Multiple calls to verify.verify() will result in an error being thrown.

Because public keys can be derived from private keys, a private key may be passed instead of a public key.

Class: X509Certificate#

Encapsulates an X509 certificate and provides read-only access to its information.

const { X509Certificate } = await import('node:crypto');

const x509 = new X509Certificate('{... pem encoded cert ...}');

console.log(x509.subject);
const { X509Certificate } = require('node:crypto');

const x509 = new X509Certificate('{... pem encoded cert ...}');

console.log(x509.subject);
javascript

new X509Certificate(buffer)#

x509.ca#

  • Type: <boolean> Will be true if this is a Certificate Authority (CA) certificate.

x509.checkEmail(email[, options])#

Checks whether the certificate matches the given email address.

If the 'subject' option is undefined or set to 'default', the certificate subject is considered according to OpenSSL's default behavior.

If the 'subject' option is set to 'always' and if the subject alternative name extension either does not exist or does not contain a matching email address, the certificate subject is considered.

If the 'subject' option is set to 'never', the certificate subject is never considered, even if the certificate contains no subject alternative names.

x509.checkHost(name[, options])#

  • name <string>
  • options <Object>
    • subject <string> 'default', 'always', or 'never'. Default: 'default'.
    • wildcards <boolean> Default: true.
    • partialWildcards <boolean> Default: true.
    • multiLabelWildcards <boolean> Default: false.
    • singleLabelSubdomains <boolean> Default: false.
  • Returns: <string> | <undefined> Returns a subject name that matches name, or undefined if no subject name matches name.

Checks whether the certificate matches the given host name.

If the certificate matches the given host name, the matching subject name is returned. The returned name might be an exact match (e.g., foo.example.com) or it might contain wildcards (e.g., *.example.com). Because host name comparisons are case-insensitive, the returned subject name might also differ from the given name in capitalization.

If the 'subject' option is undefined or set to 'default', the certificate subject is considered according to OpenSSL's default behavior.

If the 'subject' option is set to 'always' and if the subject alternative name extension either does not exist or does not contain a matching DNS name, the certificate subject is considered.

If the 'subject' option is set to 'never', the certificate subject is never considered, even if the certificate contains no subject alternative names.

x509.checkIP(ip)#

Checks whether the certificate matches the given IP address (IPv4 or IPv6).

Only RFC 5280 iPAddress subject alternative names are considered, and they must match the given ip address exactly. Other subject alternative names as well as the subject field of the certificate are ignored.

x509.checkIssued(otherCert)#

Checks whether this certificate was potentially issued by the given otherCert by comparing the certificate metadata.

This is useful for pruning a list of possible issuer certificates which have been selected using a more rudimentary filtering routine, i.e. just based on subject and issuer names.

Finally, to verify that this certificate's signature was produced by a private key corresponding to otherCert's public key use x509.verify(publicKey) with otherCert's public key represented as a KeyObject like so

if (!x509.verify(otherCert.publicKey)) {
  throw new Error('otherCert did not issue x509');
}
js

x509.checkPrivateKey(privateKey)#

Checks whether the public key for this certificate is consistent with the given private key.

x509.fingerprint#

The SHA-1 fingerprint of this certificate.

Because SHA-1 is cryptographically broken and because the security of SHA-1 is significantly worse than that of algorithms that are commonly used to sign certificates, consider using x509.fingerprint256 instead.

x509.fingerprint256#

The SHA-256 fingerprint of this certificate.

x509.fingerprint512#

The SHA-512 fingerprint of this certificate.

Because computing the SHA-256 fingerprint is usually faster and because it is only half the size of the SHA-512 fingerprint, x509.fingerprint256 may be a better choice. While SHA-512 presumably provides a higher level of security in general, the security of SHA-256 matches that of most algorithms that are commonly used to sign certificates.

x509.infoAccess#

A textual representation of the certificate's authority information access extension.

This is a line feed separated list of access descriptions. Each line begins with the access method and the kind of the access location, followed by a colon and the value associated with the access location.

After the prefix denoting the access method and the kind of the access location, the remainder of each line might be enclosed in quotes to indicate that the value is a JSON string literal. For backward compatibility, Node.js only uses JSON string literals within this property when necessary to avoid ambiguity. Third-party code should be prepared to handle both possible entry formats.

x509.issuer#

The issuer identification included in this certificate.

x509.issuerCertificate#

The issuer certificate or undefined if the issuer certificate is not available.

x509.keyUsage#

An array detailing the key extended usages for this certificate.

x509.publicKey#

The public key <KeyObject> for this certificate.

x509.raw#

A Buffer containing the DER encoding of this certificate.

x509.serialNumber#

The serial number of this certificate.

Serial numbers are assigned by certificate authorities and do not uniquely identify certificates. Consider using x509.fingerprint256 as a unique identifier instead.

x509.subject#

The complete subject of this certificate.

x509.subjectAltName#

The subject alternative name specified for this certificate.

This is a comma-separated list of subject alternative names. Each entry begins with a string identifying the kind of the subject alternative name followed by a colon and the value associated with the entry.

Earlier versions of Node.js incorrectly assumed that it is safe to split this property at the two-character sequence ', ' (see CVE-2021-44532). However, both malicious and legitimate certificates can contain subject alternative names that include this sequence when represented as a string.

After the prefix denoting the type of the entry, the remainder of each entry might be enclosed in quotes to indicate that the value is a JSON string literal. For backward compatibility, Node.js only uses JSON string literals within this property when necessary to avoid ambiguity. Third-party code should be prepared to handle both possible entry formats.

x509.toJSON()#

There is no standard JSON encoding for X509 certificates. The toJSON() method returns a string containing the PEM encoded certificate.

x509.toLegacyObject()#

Returns information about this certificate using the legacy certificate object encoding.

x509.toString()#

Returns the PEM-encoded certificate.

x509.validFrom#

The date/time from which this certificate is valid.

x509.validFromDate#

The date/time from which this certificate is valid, encapsulated in a Date object.

x509.validTo#

The date/time until which this certificate is valid.

x509.validToDate#

The date/time until which this certificate is valid, encapsulated in a Date object.

x509.signatureAlgorithm#

The algorithm used to sign the certificate or undefined if the signature algorithm is unknown by OpenSSL.

x509.signatureAlgorithmOid#

The OID of the algorithm used to sign the certificate.

x509.verify(publicKey)#

Verifies that this certificate was signed by the given public key. Does not perform any other validation checks on the certificate.

node:crypto module methods and properties#

crypto.argon2(algorithm, parameters, callback)#

  • algorithm <string> Variant of Argon2, one of "argon2d", "argon2i" or "argon2id".
  • parameters <Object>
    • message <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> REQUIRED, this is the password for password hashing applications of Argon2.
    • nonce <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> REQUIRED, must be at least 8 bytes long. This is the salt for password hashing applications of Argon2.
    • parallelism <number> REQUIRED, degree of parallelism determines how many computational chains (lanes) can be run. Must be at least 1 and at most 2**24-1.
    • tagLength <number> REQUIRED, the length of the key to generate. Must be at least 4 and at most 2**32-1.
    • memory <number> REQUIRED, memory cost in 1KiB blocks. Must be at least 8 * parallelism and at most 2**32-1. The actual number of blocks is rounded down to the nearest multiple of 4 * parallelism.
    • passes <number> REQUIRED, number of passes (iterations). Must be at least 1 and at most 2**32-1.
    • secret <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> | <undefined> OPTIONAL, Random additional input, similar to the salt, that should NOT be stored with the derived key. This is known as pepper in password hashing applications. If used, must have a length not greater than 2**32-1 bytes.
    • associatedData <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> | <undefined> OPTIONAL, Additional data to be added to the hash, functionally equivalent to salt or secret, but meant for non-random data. If used, must have a length not greater than 2**32-1 bytes.
  • callback <Function>

Provides an asynchronous Argon2 implementation. Argon2 is a password-based key derivation function that is designed to be expensive computationally and memory-wise in order to make brute-force attacks unrewarding.

The nonce should be as unique as possible. It is recommended that a nonce is random and at least 16 bytes long. See NIST SP 800-132 for details.

When passing strings for message, nonce, secret or associatedData, please consider caveats when using strings as inputs to cryptographic APIs.

The callback function is called with two arguments: err and derivedKey. err is an exception object when key derivation fails, otherwise err is null. derivedKey is passed to the callback as a Buffer.

An exception is thrown when any of the input arguments specify invalid values or types.

const { argon2, randomBytes } = await import('node:crypto');

const parameters = {
  message: 'password',
  nonce: randomBytes(16),
  parallelism: 4,
  tagLength: 64,
  memory: 65536,
  passes: 3,
};

argon2('argon2id', parameters, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // 'af91dad...9520f15'
});
const { argon2, randomBytes } = require('node:crypto');

const parameters = {
  message: 'password',
  nonce: randomBytes(16),
  parallelism: 4,
  tagLength: 64,
  memory: 65536,
  passes: 3,
};

argon2('argon2id', parameters, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // 'af91dad...9520f15'
});
javascript

crypto.argon2Sync(algorithm, parameters)#

  • algorithm <string> Variant of Argon2, one of "argon2d", "argon2i" or "argon2id".
  • parameters <Object>
    • message <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> REQUIRED, this is the password for password hashing applications of Argon2.
    • nonce <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> REQUIRED, must be at least 8 bytes long. This is the salt for password hashing applications of Argon2.
    • parallelism <number> REQUIRED, degree of parallelism determines how many computational chains (lanes) can be run. Must be at least 1 and at most 2**24-1.
    • tagLength <number> REQUIRED, the length of the key to generate. Must be at least 4 and at most 2**32-1.
    • memory <number> REQUIRED, memory cost in 1KiB blocks. Must be at least 8 * parallelism and at most 2**32-1. The actual number of blocks is rounded down to the nearest multiple of 4 * parallelism.
    • passes <number> REQUIRED, number of passes (iterations). Must be at least 1 and at most 2**32-1.
    • secret <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> | <undefined> OPTIONAL, Random additional input, similar to the salt, that should NOT be stored with the derived key. This is known as pepper in password hashing applications. If used, must have a length not greater than 2**32-1 bytes.
    • associatedData <string> | <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> | <undefined> OPTIONAL, Additional data to be added to the hash, functionally equivalent to salt or secret, but meant for non-random data. If used, must have a length not greater than 2**32-1 bytes.
  • Returns: <Buffer>

Provides a synchronous Argon2 implementation. Argon2 is a password-based key derivation function that is designed to be expensive computationally and memory-wise in order to make brute-force attacks unrewarding.

The nonce should be as unique as possible. It is recommended that a nonce is random and at least 16 bytes long. See NIST SP 800-132 for details.

When passing strings for message, nonce, secret or associatedData, please consider caveats when using strings as inputs to cryptographic APIs.

An exception is thrown when key derivation fails, otherwise the derived key is returned as a Buffer.

An exception is thrown when any of the input arguments specify invalid values or types.

const { argon2Sync, randomBytes } = await import('node:crypto');

const parameters = {
  message: 'password',
  nonce: randomBytes(16),
  parallelism: 4,
  tagLength: 64,
  memory: 65536,
  passes: 3,
};

const derivedKey = argon2Sync('argon2id', parameters);
console.log(derivedKey.toString('hex'));  // 'af91dad...9520f15'
const { argon2Sync, randomBytes } = require('node:crypto');

const parameters = {
  message: 'password',
  nonce: randomBytes(16),
  parallelism: 4,
  tagLength: 64,
  memory: 65536,
  passes: 3,
};

const derivedKey = argon2Sync('argon2id', parameters);
console.log(derivedKey.toString('hex'));  // 'af91dad...9520f15'
javascript

crypto.checkPrime(candidate[, options], callback)#

  • candidate <ArrayBuffer> | <SharedArrayBuffer> | <TypedArray> | <Buffer> | <DataView> | <bigint> A possible prime encoded as a sequence of big endian octets of arbitrary length.
  • options <Object>
    • checks <number> The number of Miller-Rabin probabilistic primality iterations to perform. When the value is 0 (zero), a number of checks is used that yields a false positive rate of at most 2-64 for random input. Care must be used when selecting a number of checks. Refer to the OpenSSL documentation for the BN_is_prime_ex function nchecks options for more details. Default: 0
  • callback <Function>
    • err <Error> Set to an <Error> object if an error occurred during check.
    • result <boolean> true if the candidate is a prime with an error probability less than 0.25 ** options.checks.

Checks the primality of the candidate.

crypto.checkPrimeSync(candidate[, options])#

  • candidate <ArrayBuffer> | <SharedArrayBuffer> | <TypedArray> | <Buffer> | <DataView> | <bigint> A possible prime encoded as a sequence of big endian octets of arbitrary length.
  • options <Object>
    • checks <number> The number of Miller-Rabin probabilistic primality iterations to perform. When the value is 0 (zero), a number of checks is used that yields a false positive rate of at most 2-64 for random input. Care must be used when selecting a number of checks. Refer to the OpenSSL documentation for the BN_is_prime_ex function nchecks options for more details. Default: 0
  • Returns: <boolean> true if the candidate is a prime with an error probability less than 0.25 ** options.checks.

Checks the primality of the candidate.

crypto.constants#

An object containing commonly used constants for crypto and security related operations. The specific constants currently defined are described in Crypto constants.

crypto.createCipheriv(algorithm, key, iv[, options])#

Creates and returns a Cipheriv object, with the given algorithm, key and initialization vector (iv).

The options argument controls cipher-specific settings and stream behavior. It is optional except when a cipher in CCM or OCB mode (e.g. 'aes-128-ccm') is used. In that case, the authTagLength option is required and specifies the length of the authentication tag in bytes, see CCM mode. In GCM mode, the authTagLength option is not required but can be used to set the length of the authentication tag that will be returned by getAuthTag() and defaults to 16 bytes. For SIV, GCM-SIV, and chacha20-poly1305, the authTagLength option defaults to 16 bytes. SIV and GCM-SIV only support 16-byte authentication tags.

The ctsMode and xtsStandard options configure parameters exposed by OpenSSL providers. They are available only with OpenSSL 3.0 or later and a provider that supports the corresponding parameter. ctsMode applies only to CBC-CTS ciphers, and xtsStandard applies only to sm4-xts. Supplying either option for an available cipher implementation that does not support it throws an ERR_CRYPTO_UNSUPPORTED_OPERATION error. See CBC-CTS mode and XTS mode for details.

The available algorithms depend on OpenSSL. crypto.getCiphers() lists the algorithms exposed by Node.js. On recent OpenSSL releases, openssl list -cipher-algorithms displays the algorithms available to OpenSSL, which can include algorithms that Node.js does not expose.

The key is the raw key used by the algorithm and iv is an initialization vector. Each may be a string, ArrayBuffer, Buffer, TypedArray, or DataView. A string key is decoded using options.encoding, which defaults to 'utf8'; a string iv is always decoded as UTF-8. The key may optionally be a KeyObject of type secret. If the cipher does not need an initialization vector, iv may be null.

When passing strings for key or iv, please consider caveats when using strings as inputs to cryptographic APIs.

Initialization vector requirements depend on the algorithm. For some algorithms, an IV must be unpredictable and unique; for others, uniqueness alone is sufficient, a fixed value is required, or no IV is used. Follow the requirements for the selected algorithm. IVs typically do not have to be secret and can be transmitted with the ciphertext.

crypto.createDecipheriv(algorithm, key, iv[, options])#

Creates and returns a Decipheriv object that uses the given algorithm, key and initialization vector (iv).

The options argument controls cipher-specific settings and stream behavior. It is optional except when a cipher in CCM or OCB mode (e.g. 'aes-128-ccm') is used. In that case, the authTagLength option is required and specifies the length of the authentication tag in bytes, see CCM mode. For GCM and chacha20-poly1305, the authTagLength option defaults to 16 bytes and must be set if a different length is used. For SIV and GCM-SIV, the authTagLength option defaults to 16 bytes and only 16-byte authentication tags are supported.

The ctsMode and xtsStandard options configure parameters exposed by OpenSSL providers. They are available only with OpenSSL 3.0 or later and a provider that supports the corresponding parameter. ctsMode applies only to CBC-CTS ciphers, and xtsStandard applies only to sm4-xts. Supplying either option for an available cipher implementation that does not support it throws an ERR_CRYPTO_UNSUPPORTED_OPERATION error. See CBC-CTS mode and XTS mode for details.

The available algorithms depend on OpenSSL. crypto.getCiphers() lists the algorithms exposed by Node.js. On recent OpenSSL releases, openssl list -cipher-algorithms displays the algorithms available to OpenSSL, which can include algorithms that Node.js does not expose.

The key is the raw key used by the algorithm and iv is an initialization vector. Each may be a string, ArrayBuffer, Buffer, TypedArray, or DataView. A string key is decoded using options.encoding, which defaults to 'utf8'; a string iv is always decoded as UTF-8. The key may optionally be a KeyObject of type secret. If the cipher does not need an initialization vector, iv may be null.

When passing strings for key or iv, please consider caveats when using strings as inputs to cryptographic APIs.

Initialization vector requirements depend on the algorithm. For some algorithms, an IV must be unpredictable and unique; for others, uniqueness alone is sufficient, a fixed value is required, or no IV is used. Follow the requirements for the selected algorithm. IVs typically do not have to be secret and can be transmitted with the ciphertext.

crypto.createDiffieHellman(prime[, primeEncoding][, generator][, generatorEncoding])#

Creates a DiffieHellman key exchange object using the supplied prime and an optional specific generator.

The generator argument can be a number, string, or Buffer. If generator is not specified, the value 2 is used.

If primeEncoding is specified, prime is expected to be a string; otherwise a Buffer, TypedArray, or DataView is expected.

If generatorEncoding is specified, generator is expected to be a string; otherwise a number, Buffer, TypedArray, or DataView is expected.

crypto.createDiffieHellman(primeLength[, generator])#

Creates a DiffieHellman key exchange object and generates a prime of primeLength bits using an optional specific numeric generator. If generator is not specified, the value 2 is used.

crypto.createDiffieHellmanGroup(name)#

An alias for crypto.getDiffieHellman()

crypto.createECDH(curveName)#

Creates an Elliptic Curve Diffie-Hellman (ECDH) key exchange object using a predefined curve specified by the curveName string. Use crypto.getCurves() to obtain a list of available curve names. On recent OpenSSL releases, openssl ecparam -list_curves will also display the name and description of each available elliptic curve.

crypto.createHash(algorithm[, options])#

Creates and returns a Hash object that can be used to generate hash digests using the given algorithm. Optional options argument controls stream behavior. For XOF hash functions such as 'shake256', the outputLength option can be used to specify the desired output length in bytes.

The functionName and customization options apply only to cSHAKE-128 and cSHAKE-256. They are supported only when Node.js is built with OpenSSL 4.0 or later and the selected provider supports the corresponding digest parameters. Strings are encoded as UTF-8, and neither strings nor byte values may contain NUL bytes. Both options default to an empty byte string. For OpenSSL's built-in providers, functionName is case-sensitive and must be '', 'TupleHash', 'ParallelHash', or 'KMAC'. Other providers can impose different restrictions. With both options empty, cSHAKE produces the same output as the corresponding SHAKE function for the same output length. cshake-128 and cshake-256 default to output lengths of 32 and 64 bytes, respectively.

When the data is small (< 5MB) and readily available, crypto.hash() is usually faster.

The available algorithms depend on the version and configuration of OpenSSL on the platform. Examples are 'sha256' and 'sha512'. Use crypto.getHashes() to obtain the list of hash algorithms available to the Node.js process.

Example: generating the sha256 sum of a file

import {
  createReadStream,
} from 'node:fs';
import { argv } from 'node:process';
const {
  createHash,
} = await import('node:crypto');

const filename = argv[2];

const hash = createHash('sha256');

const input = createReadStream(filename);
input.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = input.read();
  if (data)
    hash.update(data);
  else {
    console.log(`${hash.digest('hex')} ${filename}`);
  }
});
const {
  createReadStream,
} = require('node:fs');
const {
  createHash,
} = require('node:crypto');
const { argv } = require('node:process');

const filename = argv[2];

const hash = createHash('sha256');

const input = createReadStream(filename);
input.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = input.read();
  if (data)
    hash.update(data);
  else {
    console.log(`${hash.digest('hex')} ${filename}`);
  }
});
javascript

crypto.createHmac(algorithm, key[, options])#

Creates and returns an Hmac object that uses the given algorithm and key. Optional options argument controls stream behavior.

The available algorithms depend on the version and configuration of OpenSSL on the platform. Examples are 'sha256' and 'sha512'. crypto.getHashes() lists algorithms available to the hashing APIs, but HMAC imposes additional restrictions, so not every listed algorithm is suitable.

The key is the HMAC key used to generate the cryptographic HMAC hash. If it is a KeyObject, its type must be secret. If it is a string, please consider caveats when using strings as inputs to cryptographic APIs. If it was obtained from a cryptographically secure source of entropy, such as crypto.randomBytes() or crypto.generateKey(), its length should not exceed the block size of algorithm (e.g., 512 bits for SHA-256).

Example: generating the sha256 HMAC of a file

import {
  createReadStream,
} from 'node:fs';
import { argv } from 'node:process';
const {
  createHmac,
} = await import('node:crypto');

const filename = argv[2];

const hmac = createHmac('sha256', 'a secret');

const input = createReadStream(filename);
input.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = input.read();
  if (data)
    hmac.update(data);
  else {
    console.log(`${hmac.digest('hex')} ${filename}`);
  }
});
const {
  createReadStream,
} = require('node:fs');
const {
  createHmac,
} = require('node:crypto');
const { argv } = require('node:process');

const filename = argv[2];

const hmac = createHmac('sha256', 'a secret');

const input = createReadStream(filename);
input.on('readable', () => {
  // Only one element is going to be produced by the
  // hash stream.
  const data = input.read();
  if (data)
    hmac.update(data);
  else {
    console.log(`${hmac.digest('hex')} ${filename}`);
  }
});
javascript

crypto.createMac(algorithm, key[, options])#

Stability: 1.2 - Release candidate

algorithm must be a non-empty provider MAC name. The MAC-specific properties listed above are extensions to the standard stream.transform options and are passed only when the selected provider implementation advertises the corresponding parameter with the expected type. A supplied MAC-specific option that the selected implementation does not support causes an error.

The following table summarizes the MAC-specific options accepted by MAC implementations in OpenSSL's built-in providers. The key argument is required for every MAC. The table lists only MAC-specific options; standard stream.transform options remain available for every family.

MAC family Required options Optional options Notes
HMAC digest None
CMAC cipher using CBC mode None
GMAC cipher using GCM mode, non-empty iv None Requires a unique IV for every message authenticated with a given key.
KMAC None customization, outputLength
BLAKE2 MAC None customization, salt, outputLength
Poly1305 None None Each key must be used for only one message.
SipHash None outputLength

outputLength configures the output size of the provider MAC. It is never implemented by computing a longer tag and truncating it. A value of 0 is passed to the provider and is accepted only when that provider can initialize and finalize the MAC with a zero-byte output. When outputLength is omitted, the provider's default output size is used and must be nonzero.

The key must contain bytes or be a KeyObject of type secret. Key length and other key requirements are determined by the selected provider implementation.

Available algorithms and their accepted parameters depend on the OpenSSL version, loaded providers, and active default property query. Use crypto.getMacs() to list fetchable MAC names. A listed name can still require options or a key with provider-specific properties.

crypto.createPrivateKey(key)#

Creates and returns a new key object containing a private key. If key is a string or Buffer, format is assumed to be 'pem'; otherwise, key must be an object with the properties described above.

If the private key is encrypted, a passphrase must be specified. The length of the passphrase is limited to 1024 bytes.

Private keys from OpenSSL STORE loaders#

Stability: 1.1 - Active development

If key is a <URL> (or an object whose key is a <URL>), the private key is loaded through an OpenSSL STORE loader. The URL is passed to OpenSSL as a URI, for example a file: URI or a provider-backed scheme such as pkcs11:. When the Permission Model is enabled, --allow-openssl-store is required.

Warning: A URI scheme does not pin an OpenSSL STORE loader or prove where the returned key came from. Node.js forwards the URI to OpenSSL, which chooses loaders according to its version and configuration. For example, OpenSSL may offer an opaque URI such as pkcs11:object=... (one without // after the scheme) to its file loader before trying the pkcs11 loader. If the complete URI is a valid local path and that file exists, it may be loaded instead. Node.js does not verify which loader supplied the key. Do not rely on a provider-specific URI scheme as proof that a key came from that provider or from a hardware device.

Configured OpenSSL STORE loaders have broad authority and may access files, devices, tokens, or the network. Access performed by a loader is not constrained by the fs.read, fs.write, or net permission scopes.

When a <URL> is used, format, type, asymmetricKeyType, and namedCurve are ignored even when those options would otherwise depend on each other, such as type with format: 'der' or namedCurve with asymmetricKeyType: 'ec'. The input is passed to the STORE loader as a URI, not handled as PEM, DER, JWK, or raw key material. passphrase is still used as the optional PIN/passphrase passed to the loader, and encoding applies if that passphrase is a string.

Use passphrase instead of embedding credentials in the URI passed to the STORE loader. Node.js redacts the URI from its own permission-denial resource and diagnostics. Errors reported by OpenSSL or a provider after loading begins may include the URI.

When properties is specified with a <URL> key, it is passed to OpenSSL as the property query for selecting the STORE loader. It is not appended to the URL and is distinct from provider-specific URI parameters.

crypto.createPublicKey(key)#

Creates and returns a new key object containing a public key. If key is a string or Buffer, format is assumed to be 'pem'; if key is a KeyObject with type 'private', the public key is derived from the given private key; otherwise, key must be an object with the properties described above.

If the format is 'pem', the 'key' may also be an X.509 certificate.

Because public keys can be derived from private keys, a private key may be passed instead of a public key. In that case, this function behaves as if crypto.createPrivateKey() had been called, except that the type of the returned KeyObject will be 'public' and that the private key cannot be extracted from the returned KeyObject. Similarly, if a KeyObject with type 'private' is given, a new KeyObject with type 'public' will be returned and it will be impossible to extract the private key from the returned object.

A store-backed private key can be used as a public key by first loading it with crypto.createPrivateKey(); a <URL> cannot be passed to crypto.createPublicKey() directly.

crypto.createSecretKey(key[, encoding])#

Creates and returns a new key object containing a secret key for symmetric encryption or Hmac.

crypto.createSign(algorithm[, options])#

Creates and returns a Sign object that uses the given algorithm. Use crypto.getHashes() to obtain the names available to the hashing APIs. The key type and signature scheme can impose additional restrictions on which digests can be used. Optional options argument controls the stream.Writable behavior.

In some cases, a Sign instance can be created using the name of a signature algorithm, such as 'RSA-SHA256', instead of a digest algorithm. This will use the corresponding digest algorithm. This does not work for all signature algorithms, such as 'ecdsa-with-SHA256', so it is best to always use digest algorithm names.

crypto.createVerify(algorithm[, options])#

Creates and returns a Verify object that uses the given algorithm. Use crypto.getHashes() to obtain the names available to the hashing APIs. The key type and signature scheme can impose additional restrictions on which digests can be used. Optional options argument controls the stream.Writable behavior.

In some cases, a Verify instance can be created using the name of a signature algorithm, such as 'RSA-SHA256', instead of a digest algorithm. This will use the corresponding digest algorithm. This does not work for all signature algorithms, such as 'ecdsa-with-SHA256', so it is best to always use digest algorithm names.

crypto.decapsulate(key, ciphertext[, callback])#

Key decapsulation using a KEM algorithm with a private key.

Supported key types and their KEM algorithms are:

  • 'rsa'2 RSA Secret Value Encapsulation
  • 'ec'3 DHKEM(P-256, HKDF-SHA256), DHKEM(P-384, HKDF-SHA256), DHKEM(P-521, HKDF-SHA256)
  • 'x25519'3 DHKEM(X25519, HKDF-SHA256)
  • 'x448'3 DHKEM(X448, HKDF-SHA512)
  • 'ml-kem-512'1 ML-KEM
  • 'ml-kem-768'1 ML-KEM
  • 'ml-kem-1024'1 ML-KEM

If key is not a KeyObject, this function behaves as if key had been passed to crypto.createPrivateKey().

If the callback function is provided this function uses libuv's threadpool.

crypto.diffieHellman(options[, callback])#

Computes the Diffie-Hellman shared secret based on a privateKey and a publicKey. Both keys must represent the same asymmetric key type and must support either the DH or ECDH operation.

If options.privateKey is not a KeyObject, this function behaves as if options.privateKey had been passed to crypto.createPrivateKey().

If options.publicKey is not a KeyObject, this function behaves as if options.publicKey had been passed to crypto.createPublicKey().

If the callback function is provided this function uses libuv's threadpool.

crypto.encapsulate(key[, callback])#

Key encapsulation using a KEM algorithm with a public key.

Supported key types and their KEM algorithms are:

  • 'rsa'2 RSA Secret Value Encapsulation
  • 'ec'3 DHKEM(P-256, HKDF-SHA256), DHKEM(P-384, HKDF-SHA256), DHKEM(P-521, HKDF-SHA256)
  • 'x25519'3 DHKEM(X25519, HKDF-SHA256)
  • 'x448'3 DHKEM(X448, HKDF-SHA512)
  • 'ml-kem-512'1 ML-KEM
  • 'ml-kem-768'1 ML-KEM
  • 'ml-kem-1024'1 ML-KEM

If key is not a KeyObject, this function behaves as if key had been passed to crypto.createPublicKey().

If the callback function is provided this function uses libuv's threadpool.

crypto.fips#

Stability: 0 - Deprecated

Deprecated property for checking and controlling FIPS mode. Use crypto.getFips() and crypto.setFips() instead.

crypto.generateKey(type, options, callback)#

  • type <string> The intended use of the generated secret key. Currently accepted values are 'hmac' and 'aes'.
  • options <Object>
    • length <number> The bit length of the key to generate. This must be a value greater than 0.
      • If type is 'hmac', the minimum is 8, and the maximum length is 231-1. If the value is not a multiple of 8, the generated key will be truncated to Math.floor(length / 8).
      • If type is 'aes', the length must be one of 128, 192, or 256.
  • callback <Function>

Asynchronously generates a new random secret key of the given length. The type will determine which validations will be performed on the length.

const {
  generateKey,
} = await import('node:crypto');

generateKey('hmac', { length: 512 }, (err, key) => {
  if (err) throw err;
  console.log(key.export().toString('hex'));  // 46e..........620
});
const {
  generateKey,
} = require('node:crypto');

generateKey('hmac', { length: 512 }, (err, key) => {
  if (err) throw err;
  console.log(key.export().toString('hex'));  // 46e..........620
});
javascript

The size of a generated HMAC key should not exceed the block size of the underlying hash function. See crypto.createHmac() for more information.

crypto.generateKeyPair(type, options, callback)#

Generates a new asymmetric key pair of the given type. See the supported asymmetric key types.

For RSA-PSS keys, crypto.getHashes() lists algorithms available to the hashing APIs, but not every listed digest can be encoded in RSA-PSS parameters or is supported by the active RSA implementation.

If a publicKeyEncoding or privateKeyEncoding was specified, this function behaves as if keyObject.export() had been called on its result. Otherwise, the respective part of the key is returned as a KeyObject.

It is recommended to encode public keys as 'spki' and private keys as 'pkcs8' with encryption for long-term storage:

const {
  generateKeyPair,
} = await import('node:crypto');

generateKeyPair('rsa', {
  modulusLength: 4096,
  publicKeyEncoding: {
    type: 'spki',
    format: 'pem',
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem',
    cipher: 'aes-256-cbc',
    passphrase: 'top secret',
  },
}, (err, publicKey, privateKey) => {
  // Handle errors and use the generated key pair.
});
const {
  generateKeyPair,
} = require('node:crypto');

generateKeyPair('rsa', {
  modulusLength: 4096,
  publicKeyEncoding: {
    type: 'spki',
    format: 'pem',
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem',
    cipher: 'aes-256-cbc',
    passphrase: 'top secret',
  },
}, (err, publicKey, privateKey) => {
  // Handle errors and use the generated key pair.
});
javascript

On completion, callback will be called with err set to undefined and publicKey / privateKey representing the generated key pair.

If this method is invoked as its util.promisify()ed version, it returns a Promise for an Object with publicKey and privateKey properties.

crypto.generateKeyPairSync(type, options)#

Generates a new asymmetric key pair of the given type. See the supported asymmetric key types.

For RSA-PSS keys, crypto.getHashes() lists algorithms available to the hashing APIs, but not every listed digest can be encoded in RSA-PSS parameters or is supported by the active RSA implementation.

If a publicKeyEncoding or privateKeyEncoding was specified, this function behaves as if keyObject.export() had been called on its result. Otherwise, the respective part of the key is returned as a KeyObject.

When encoding public keys, it is recommended to use 'spki'. When encoding private keys, it is recommended to use 'pkcs8' with a strong passphrase, and to keep the passphrase confidential.

const {
  generateKeyPairSync,
} = await import('node:crypto');

const {
  publicKey,
  privateKey,
} = generateKeyPairSync('rsa', {
  modulusLength: 4096,
  publicKeyEncoding: {
    type: 'spki',
    format: 'pem',
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem',
    cipher: 'aes-256-cbc',
    passphrase: 'top secret',
  },
});
const {
  generateKeyPairSync,
} = require('node:crypto');

const {
  publicKey,
  privateKey,
} = generateKeyPairSync('rsa', {
  modulusLength: 4096,
  publicKeyEncoding: {
    type: 'spki',
    format: 'pem',
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem',
    cipher: 'aes-256-cbc',
    passphrase: 'top secret',
  },
});
javascript

The return value { publicKey, privateKey } represents the generated key pair. When PEM encoding was selected, the respective key will be a string, otherwise it will be a buffer containing the data encoded as DER.

crypto.generateKeySync(type, options)#

  • type <string> The intended use of the generated secret key. Currently accepted values are 'hmac' and 'aes'.
  • options <Object>
    • length <number> The bit length of the key to generate.
      • If type is 'hmac', the minimum is 8, and the maximum length is 231-1. If the value is not a multiple of 8, the generated key will be truncated to Math.floor(length / 8).
      • If type is 'aes', the length must be one of 128, 192, or 256.
  • Returns: <KeyObject>

Synchronously generates a new random secret key of the given length. The type will determine which validations will be performed on the length.

const {
  generateKeySync,
} = await import('node:crypto');

const key = generateKeySync('hmac', { length: 512 });
console.log(key.export().toString('hex'));  // e89..........41e
const {
  generateKeySync,
} = require('node:crypto');

const key = generateKeySync('hmac', { length: 512 });
console.log(key.export().toString('hex'));  // e89..........41e
javascript

The size of a generated HMAC key should not exceed the block size of the underlying hash function. See crypto.createHmac() for more information.

crypto.generatePrime(size[, options], callback)#

Generates a pseudorandom prime of size bits.

If options.safe is true, the prime will be a safe prime -- that is, (prime - 1) / 2 will also be a prime.

The options.add and options.rem parameters can be used to enforce additional requirements, e.g., for Diffie-Hellman:

  • If options.add and options.rem are both set, the prime will satisfy the condition that prime % add = rem.
  • If only options.add is set and options.safe is not true, the prime will satisfy the condition that prime % add = 1.
  • If only options.add is set and options.safe is set to true, the prime will instead satisfy the condition that prime % add = 3. This is necessary because prime % add = 1 for options.add > 2 would contradict the condition enforced by options.safe.
  • options.rem is ignored if options.add is not given.

Both options.add and options.rem must be encoded as big-endian sequences if given as an ArrayBuffer, SharedArrayBuffer, TypedArray, Buffer, or DataView.

By default, the prime is encoded as a big-endian sequence of octets in an <ArrayBuffer>. If the bigint option is true, then a <bigint> is provided.

The size of the prime will have a direct impact on how long it takes to generate the prime. The larger the size, the longer it will take. Because we use OpenSSL's BN_generate_prime_ex function, which provides only minimal control over our ability to interrupt the generation process, it is not recommended to generate overly large primes, as doing so may make the process unresponsive.

crypto.generatePrimeSync(size[, options])#

Generates a pseudorandom prime of size bits.

If options.safe is true, the prime will be a safe prime -- that is, (prime - 1) / 2 will also be a prime.

The options.add and options.rem parameters can be used to enforce additional requirements, e.g., for Diffie-Hellman:

  • If options.add and options.rem are both set, the prime will satisfy the condition that prime % add = rem.
  • If only options.add is set and options.safe is not true, the prime will satisfy the condition that prime % add = 1.
  • If only options.add is set and options.safe is set to true, the prime will instead satisfy the condition that prime % add = 3. This is necessary because prime % add = 1 for options.add > 2 would contradict the condition enforced by options.safe.
  • options.rem is ignored if options.add is not given.

Both options.add and options.rem must be encoded as big-endian sequences if given as an ArrayBuffer, SharedArrayBuffer, TypedArray, Buffer, or DataView.

By default, the prime is encoded as a big-endian sequence of octets in an <ArrayBuffer>. If the bigint option is true, then a <bigint> is provided.

The size of the prime will have a direct impact on how long it takes to generate the prime. The larger the size, the longer it will take. Because we use OpenSSL's BN_generate_prime_ex function, which provides only minimal control over our ability to interrupt the generation process, it is not recommended to generate overly large primes, as doing so may make the process unresponsive.

crypto.getCipherInfo(nameOrNid[, options])#

  • nameOrNid <string> | <number> The name or nid of the cipher to query.
  • options <Object>
  • Returns: <Object>
    • name <string> The name of the cipher
    • nid <number> | <undefined> The nid of the cipher. This property is undefined if the cipher has no OpenSSL nid.
    • blockSize <number> | <undefined> The block size of the cipher in bytes. This property is undefined when mode is 'stream'.
    • ivLength <number> | <undefined> The expected or default initialization vector length in bytes. This property is undefined if the cipher does not use an initialization vector.
    • keyLength <number> The expected or default key length in bytes.
    • mode <string> The cipher mode. One of 'cbc', 'ccm', 'cfb', 'ctr', 'ecb', 'gcm', 'gcm-siv', 'ocb', 'ofb', 'siv', 'stream', 'wrap', 'xts'.

Returns information about a given cipher.

Some ciphers accept variable length keys and initialization vectors. By default, the crypto.getCipherInfo() method will return the default values for these ciphers. To test if a given key length or iv length is acceptable for given cipher, use the keyLength and ivLength options. If the given values are unacceptable, undefined will be returned.

crypto.getCiphers()#

  • Returns: <string>[] An array with the names of the supported cipher algorithms.
const {
  getCiphers,
} = await import('node:crypto');

console.log(getCiphers()); // ['aes-128-cbc', 'aes-128-ccm', ...]
const {
  getCiphers,
} = require('node:crypto');

console.log(getCiphers()); // ['aes-128-cbc', 'aes-128-ccm', ...]
javascript

crypto.getCurves()#

  • Returns: <string>[] An array with the names of the supported elliptic curves.
const {
  getCurves,
} = await import('node:crypto');

console.log(getCurves()); // ['Oakley-EC2N-3', 'Oakley-EC2N-4', ...]
const {
  getCurves,
} = require('node:crypto');

console.log(getCurves()); // ['Oakley-EC2N-3', 'Oakley-EC2N-4', ...]
javascript

crypto.getDiffieHellman(groupName)#

Creates a predefined DiffieHellmanGroup key exchange object. The supported groups are listed in the documentation for DiffieHellmanGroup.

The returned object mimics the interface of objects created by crypto.createDiffieHellman(), but will not allow changing the keys (with diffieHellman.setPublicKey(), for example). The advantage of using this method is that the parties do not have to generate nor exchange a group modulus beforehand, saving both processor and communication time.

Example (obtaining a shared secret):

const {
  getDiffieHellman,
} = await import('node:crypto');
const alice = getDiffieHellman('modp14');
const bob = getDiffieHellman('modp14');

alice.generateKeys();
bob.generateKeys();

const aliceSecret = alice.computeSecret(bob.getPublicKey(), null, 'hex');
const bobSecret = bob.computeSecret(alice.getPublicKey(), null, 'hex');

/* aliceSecret and bobSecret should be the same */
console.log(aliceSecret === bobSecret);
const {
  getDiffieHellman,
} = require('node:crypto');

const alice = getDiffieHellman('modp14');
const bob = getDiffieHellman('modp14');

alice.generateKeys();
bob.generateKeys();

const aliceSecret = alice.computeSecret(bob.getPublicKey(), null, 'hex');
const bobSecret = bob.computeSecret(alice.getPublicKey(), null, 'hex');

/* aliceSecret and bobSecret should be the same */
console.log(aliceSecret === bobSecret);
javascript

crypto.getFips()#

  • Returns: <number> 1 if FIPS mode is enabled, 0 otherwise. A future semver-major release may change the return type of this API to a <boolean>.

With OpenSSL 3, this reports whether the default property query includes fips=yes. It does not establish that a FIPS provider is loaded or validated. It can return 1 even when a requested cryptographic implementation cannot be fetched because no loaded provider supplies a match for fips=yes. See FIPS mode.

crypto.getHashes()#

  • Returns: <string>[] An array of the names of the supported hash algorithms, such as 'RSA-SHA256'. Hash algorithms are also called "digest" algorithms.

This is the authoritative Node.js list of hash algorithms available to crypto.createHash() and crypto.hash() in the current process. With OpenSSL 3 or later, the list depends on the loaded providers and the default property query in effect when the list is first generated. Some listed algorithms can require API-specific options, such as outputLength for XOF hash functions.

A listed hash algorithm is not necessarily supported by APIs that combine a digest with another cryptographic operation, such as HMAC, key derivation, or signing. Those operations can apply additional restrictions.

const {
  getHashes,
} = await import('node:crypto');

console.log(getHashes()); // ['DSA', 'DSA-SHA', 'DSA-SHA1', ...]
const {
  getHashes,
} = require('node:crypto');

console.log(getHashes()); // ['DSA', 'DSA-SHA', 'DSA-SHA1', ...]
javascript

crypto.getMacs()#

Stability: 1.2 - Release candidate

  • Returns: <string>[] A fresh array containing the sorted, lowercase names and aliases of fetchable MAC implementations.

Returns MAC names exposed by loaded OpenSSL providers that match the active default property query. Duplicate names and numeric OID aliases are omitted. On builds without OpenSSL EVP_MAC support, this function returns an empty array.

The returned names describe implementations that OpenSSL can fetch. They do not guarantee that crypto.createMac() can initialize the MAC without additional options. A provider can require additional parameters or a key with algorithm-specific properties, and it can expose parameters that this API does not support.

After a successful FIPS mode change made with crypto.setFips(), subsequent calls reflect the new mode, and newly created Mac objects use it. Existing Mac objects continue using the provider implementation selected when they were created.

const { getMacs } = await import('node:crypto');

console.log(getMacs());
// ['blake2bmac', 'blake2smac', 'cmac', 'gmac', 'hmac', ...]
mjs

crypto.getRandomValues(typedArray)#

A convenient alias for crypto.webcrypto.getRandomValues(). This implementation is not compliant with the Web Crypto spec, to write web-compatible code use crypto.webcrypto.getRandomValues() instead.

crypto.hash(algorithm, data[, options])#

A utility for creating one-shot hash digests of data. It can be faster than the object-based crypto.createHash() when hashing a smaller amount of data (<= 5MB) that's readily available. If the data can be big or if it is streamed, it's still recommended to use crypto.createHash() instead.

The available algorithms depend on the version and configuration of OpenSSL on the platform. Examples are 'sha256' and 'sha512'. Use crypto.getHashes() to obtain the list of hash algorithms available to the Node.js process.

The functionName and customization options apply only to cSHAKE-128 and cSHAKE-256. They are supported only when Node.js is built with OpenSSL 4.0 or later and the selected provider supports the corresponding digest parameters. Strings are encoded as UTF-8, and neither strings nor byte values may contain NUL bytes. Both options default to an empty byte string. For OpenSSL's built-in providers, functionName is case-sensitive and must be '', 'TupleHash', 'ParallelHash', or 'KMAC'. Other providers can impose different restrictions. With both options empty, cSHAKE produces the same output as the corresponding SHAKE function for the same output length. cshake-128 and cshake-256 default to output lengths of 32 and 64 bytes, respectively.

If options is a string, then it specifies the outputEncoding.

Example:

const crypto = require('node:crypto');
const { Buffer } = require('node:buffer');

// Hashing a string and return the result as a hex-encoded string.
const string = 'Node.js';
// 10b3493287f831e81a438811a1ffba01f8cec4b7
console.log(crypto.hash('sha1', string));

// Encode a base64-encoded string into a Buffer, hash it and return
// the result as a buffer.
const base64 = 'Tm9kZS5qcw==';
// <Buffer 10 b3 49 32 87 f8 31 e8 1a 43 88 11 a1 ff ba 01 f8 ce c4 b7>
console.log(crypto.hash('sha1', Buffer.from(base64, 'base64'), 'buffer'));
import crypto from 'node:crypto';
import { Buffer } from 'node:buffer';

// Hashing a string and return the result as a hex-encoded string.
const string = 'Node.js';
// 10b3493287f831e81a438811a1ffba01f8cec4b7
console.log(crypto.hash('sha1', string));

// Encode a base64-encoded string into a Buffer, hash it and return
// the result as a buffer.
const base64 = 'Tm9kZS5qcw==';
// <Buffer 10 b3 49 32 87 f8 31 e8 1a 43 88 11 a1 ff ba 01 f8 ce c4 b7>
console.log(crypto.hash('sha1', Buffer.from(base64, 'base64'), 'buffer'));
javascript

crypto.hkdf(digest, ikm, salt, info, keylen, callback)#

HKDF is a simple key derivation function defined in RFC 5869. The given ikm, salt and info are used with the digest to derive a key of keylen bytes. The available digest algorithms depend on the version and configuration of OpenSSL. HKDF uses HMAC internally. crypto.getHashes() lists algorithms available to the hashing APIs, but not every listed algorithm is necessarily suitable for HMAC or HKDF.

The supplied callback function is called with two arguments: err and derivedKey. If an error occurs while deriving the key, err will be set; otherwise err will be null. The successfully generated derivedKey will be passed to the callback as an <ArrayBuffer>. An error will be thrown if any of the input arguments specify invalid values or types.

import { Buffer } from 'node:buffer';
const {
  hkdf,
} = await import('node:crypto');

hkdf('sha512', 'key', 'salt', 'info', 64, (err, derivedKey) => {
  if (err) throw err;
  console.log(Buffer.from(derivedKey).toString('hex'));  // '24156e2...5391653'
});
const {
  hkdf,
} = require('node:crypto');
const { Buffer } = require('node:buffer');

hkdf('sha512', 'key', 'salt', 'info', 64, (err, derivedKey) => {
  if (err) throw err;
  console.log(Buffer.from(derivedKey).toString('hex'));  // '24156e2...5391653'
});
javascript

crypto.hkdfSync(digest, ikm, salt, info, keylen)#

Provides a synchronous HKDF key derivation function as defined in RFC 5869. The given ikm, salt and info are used with the digest to derive a key of keylen bytes. The available digest algorithms depend on the version and configuration of OpenSSL. HKDF uses HMAC internally. crypto.getHashes() lists algorithms available to the hashing APIs, but not every listed algorithm is necessarily suitable for HMAC or HKDF.

The successfully generated derivedKey will be returned as an <ArrayBuffer>.

An error will be thrown if any of the input arguments specify invalid values or types, or if the derived key cannot be generated.

import { Buffer } from 'node:buffer';
const {
  hkdfSync,
} = await import('node:crypto');

const derivedKey = hkdfSync('sha512', 'key', 'salt', 'info', 64);
console.log(Buffer.from(derivedKey).toString('hex'));  // '24156e2...5391653'
const {
  hkdfSync,
} = require('node:crypto');
const { Buffer } = require('node:buffer');

const derivedKey = hkdfSync('sha512', 'key', 'salt', 'info', 64);
console.log(Buffer.from(derivedKey).toString('hex'));  // '24156e2...5391653'
javascript

crypto.parsePKCS12(bundle[, options])#

Parses a PKCS#12 bundle, commonly stored with a .p12 or .pfx extension, and returns its private key and certificates.

import { parsePKCS12 } from 'node:crypto';
import { readFileSync } from 'node:fs';

const { privateKey, certificate, additionalCertificates } = parsePKCS12(
  readFileSync('bundle.p12'),
  { passphrase: 'secret' },
);
mjs

crypto.pbkdf2(password, salt, iterations, keylen, digest, callback)#

Provides an asynchronous Password-Based Key Derivation Function 2 (PBKDF2) implementation. A selected HMAC digest algorithm specified by digest is applied to derive a key of the requested byte length (keylen) from the password, salt and iterations.

The supplied callback function is called with two arguments: err and derivedKey. If an error occurs while deriving the key, err will be set; otherwise err will be null. By default, the successfully generated derivedKey will be passed to the callback as a Buffer. An error will be thrown if any of the input arguments specify invalid values or types.

The iterations argument must be a number set as high as possible. The higher the number of iterations, the more secure the derived key will be, but will take a longer amount of time to complete.

The salt should be as unique as possible. It is recommended that a salt is random and at least 16 bytes long. See NIST SP 800-132 for details.

When passing strings for password or salt, please consider caveats when using strings as inputs to cryptographic APIs.

const {
  pbkdf2,
} = await import('node:crypto');

pbkdf2('secret', 'salt', 100000, 64, 'sha512', (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...08d59ae'
});
const {
  pbkdf2,
} = require('node:crypto');

pbkdf2('secret', 'salt', 100000, 64, 'sha512', (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...08d59ae'
});
javascript

The available digest algorithms depend on the version and configuration of OpenSSL. PBKDF2 uses HMAC internally. crypto.getHashes() lists algorithms available to the hashing APIs, but not every listed algorithm is necessarily suitable for HMAC or PBKDF2.

This API uses libuv's threadpool, which can have surprising and negative performance implications for some applications; see the UV_THREADPOOL_SIZE documentation for more information.

crypto.pbkdf2Sync(password, salt, iterations, keylen, digest)#

Provides a synchronous Password-Based Key Derivation Function 2 (PBKDF2) implementation. A selected HMAC digest algorithm specified by digest is applied to derive a key of the requested byte length (keylen) from the password, salt and iterations.

If an error occurs an Error will be thrown, otherwise the derived key will be returned as a Buffer.

The iterations argument must be a number set as high as possible. The higher the number of iterations, the more secure the derived key will be, but will take a longer amount of time to complete.

The salt should be as unique as possible. It is recommended that a salt is random and at least 16 bytes long. See NIST SP 800-132 for details.

When passing strings for password or salt, please consider caveats when using strings as inputs to cryptographic APIs.

const {
  pbkdf2Sync,
} = await import('node:crypto');

const key = pbkdf2Sync('secret', 'salt', 100000, 64, 'sha512');
console.log(key.toString('hex'));  // '3745e48...08d59ae'
const {
  pbkdf2Sync,
} = require('node:crypto');

const key = pbkdf2Sync('secret', 'salt', 100000, 64, 'sha512');
console.log(key.toString('hex'));  // '3745e48...08d59ae'
javascript

The available digest algorithms depend on the version and configuration of OpenSSL. PBKDF2 uses HMAC internally. crypto.getHashes() lists algorithms available to the hashing APIs, but not every listed algorithm is necessarily suitable for HMAC or PBKDF2.

crypto.privateDecrypt(privateKey, buffer)#

Decrypts buffer with privateKey. buffer was previously encrypted using the corresponding public key, for example using crypto.publicEncrypt().

crypto.getHashes() lists algorithms available to the hashing APIs, but the active RSA implementation can impose additional restrictions on digests used for OAEP or MGF1.

If privateKey is not a KeyObject, this function behaves as if privateKey had been passed to crypto.createPrivateKey(). If it is an object, the padding property can be passed. Otherwise, this function uses RSA_PKCS1_OAEP_PADDING.

Using crypto.constants.RSA_PKCS1_PADDING in crypto.privateDecrypt() requires OpenSSL to support implicit rejection (rsa_pkcs1_implicit_rejection). If the version of OpenSSL used by Node.js does not support this feature, attempting to use RSA_PKCS1_PADDING will fail.

crypto.privateEncrypt(privateKey, buffer)#

Encrypts buffer with privateKey. The returned data can be decrypted using the corresponding public key, for example using crypto.publicDecrypt().

If privateKey is not a KeyObject, this function behaves as if privateKey had been passed to crypto.createPrivateKey(). If it is an object, the padding property can be passed. Otherwise, this function uses RSA_PKCS1_PADDING.

crypto.publicDecrypt(key, buffer)#

Decrypts buffer with key. buffer was previously encrypted using the corresponding private key, for example using crypto.privateEncrypt().

If key is not a KeyObject, this function behaves as if key had been passed to crypto.createPublicKey(). If it is an object, the padding property can be passed. Otherwise, this function uses RSA_PKCS1_PADDING.

Because RSA public keys can be derived from private keys, a private key may be passed instead of a public key.

crypto.publicEncrypt(key, buffer)#

Encrypts the content of buffer with key and returns a new Buffer with encrypted content. The returned data can be decrypted using the corresponding private key, for example using crypto.privateDecrypt().

crypto.getHashes() lists algorithms available to the hashing APIs, but the active RSA implementation can impose additional restrictions on digests used for OAEP or MGF1.

If key is not a KeyObject, this function behaves as if key had been passed to crypto.createPublicKey(). If it is an object, the padding property can be passed. Otherwise, this function uses RSA_PKCS1_OAEP_PADDING.

Because RSA public keys can be derived from private keys, a private key may be passed instead of a public key.

crypto.randomBytes(size[, callback])#

Generates cryptographically strong pseudorandom data. The size argument is a number indicating the number of bytes to generate.

If a callback function is provided, the bytes are generated asynchronously and the callback function is invoked with two arguments: err and buf. If an error occurs, err will be an Error object; otherwise it is null. The buf argument is a Buffer containing the generated bytes.

// Asynchronous
const {
  randomBytes,
} = await import('node:crypto');

randomBytes(256, (err, buf) => {
  if (err) throw err;
  console.log(`${buf.length} bytes of random data: ${buf.toString('hex')}`);
});
// Asynchronous
const {
  randomBytes,
} = require('node:crypto');

randomBytes(256, (err, buf) => {
  if (err) throw err;
  console.log(`${buf.length} bytes of random data: ${buf.toString('hex')}`);
});
javascript

If the callback function is not provided, the random bytes are generated synchronously and returned as a Buffer. An error will be thrown if there is a problem generating the bytes.

// Synchronous
const {
  randomBytes,
} = await import('node:crypto');

const buf = randomBytes(256);
console.log(
  `${buf.length} bytes of random data: ${buf.toString('hex')}`);
// Synchronous
const {
  randomBytes,
} = require('node:crypto');

const buf = randomBytes(256);
console.log(
  `${buf.length} bytes of random data: ${buf.toString('hex')}`);
javascript

The crypto.randomBytes() method will not complete until there is sufficient entropy available. This should normally never take longer than a few milliseconds. The only time when generating the random bytes may conceivably block for a longer period of time is right after boot, when the whole system is still low on entropy.

This API uses libuv's threadpool, which can have surprising and negative performance implications for some applications; see the UV_THREADPOOL_SIZE documentation for more information.

The asynchronous version of crypto.randomBytes() is carried out in a single threadpool request. To minimize threadpool task length variation, partition large randomBytes requests when doing so as part of fulfilling a client request.

crypto.randomFill(buffer[, offset][, size], callback)#

  • buffer <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> Must be supplied. The size of the provided buffer must not be larger than 2**31 - 1.
  • offset <number> The start position, in elements for a TypedArray and in bytes for an ArrayBuffer or DataView. Default: 0
  • size <number> The amount to fill, in the same units as offset. Default: buffer.length - offset for a TypedArray, or buffer.byteLength - offset for an ArrayBuffer or DataView. The size must not be larger than 2**31 - 1.
  • callback <Function> function(err, buf) {}.

This function is similar to crypto.randomBytes() but requires the first argument to be a Buffer that will be filled. It also requires that a callback is passed in.

If the callback function is not provided, an error will be thrown.

import { Buffer } from 'node:buffer';
const { randomFill } = await import('node:crypto');

const buf = Buffer.alloc(10);
randomFill(buf, (err, buf) => {
  if (err) throw err;
  console.log(buf.toString('hex'));
});

randomFill(buf, 5, (err, buf) => {
  if (err) throw err;
  console.log(buf.toString('hex'));
});

// The above is equivalent to the following:
randomFill(buf, 5, 5, (err, buf) => {
  if (err) throw err;
  console.log(buf.toString('hex'));
});
const { randomFill } = require('node:crypto');
const { Buffer } = require('node:buffer');

const buf = Buffer.alloc(10);
randomFill(buf, (err, buf) => {
  if (err) throw err;
  console.log(buf.toString('hex'));
});

randomFill(buf, 5, (err, buf) => {
  if (err) throw err;
  console.log(buf.toString('hex'));
});

// The above is equivalent to the following:
randomFill(buf, 5, 5, (err, buf) => {
  if (err) throw err;
  console.log(buf.toString('hex'));
});
javascript

Any ArrayBuffer, TypedArray, or DataView instance may be passed as buffer.

While this includes instances of Float32Array and Float64Array, this function should not be used to generate random floating-point numbers. The result may contain +Infinity, -Infinity, and NaN, and even if the array contains finite numbers only, they are not drawn from a uniform random distribution and have no meaningful lower or upper bounds.

import { Buffer } from 'node:buffer';
const { randomFill } = await import('node:crypto');

const a = new Uint32Array(10);
randomFill(a, (err, buf) => {
  if (err) throw err;
  console.log(Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength)
    .toString('hex'));
});

const b = new DataView(new ArrayBuffer(10));
randomFill(b, (err, buf) => {
  if (err) throw err;
  console.log(Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength)
    .toString('hex'));
});

const c = new ArrayBuffer(10);
randomFill(c, (err, buf) => {
  if (err) throw err;
  console.log(Buffer.from(buf).toString('hex'));
});
const { randomFill } = require('node:crypto');
const { Buffer } = require('node:buffer');

const a = new Uint32Array(10);
randomFill(a, (err, buf) => {
  if (err) throw err;
  console.log(Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength)
    .toString('hex'));
});

const b = new DataView(new ArrayBuffer(10));
randomFill(b, (err, buf) => {
  if (err) throw err;
  console.log(Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength)
    .toString('hex'));
});

const c = new ArrayBuffer(10);
randomFill(c, (err, buf) => {
  if (err) throw err;
  console.log(Buffer.from(buf).toString('hex'));
});
javascript

This API uses libuv's threadpool, which can have surprising and negative performance implications for some applications; see the UV_THREADPOOL_SIZE documentation for more information.

The asynchronous version of crypto.randomFill() is carried out in a single threadpool request. To minimize threadpool task length variation, partition large randomFill requests when doing so as part of fulfilling a client request.

crypto.randomFillSync(buffer[, offset][, size])#

  • buffer <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> Must be supplied. The size of the provided buffer must not be larger than 2**31 - 1.
  • offset <number> The start position, in elements for a TypedArray and in bytes for an ArrayBuffer or DataView. Default: 0
  • size <number> The amount to fill, in the same units as offset. Default: buffer.length - offset for a TypedArray, or buffer.byteLength - offset for an ArrayBuffer or DataView. The size must not be larger than 2**31 - 1.
  • Returns: <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> The object passed as buffer argument.

Synchronous version of crypto.randomFill().

import { Buffer } from 'node:buffer';
const { randomFillSync } = await import('node:crypto');

const buf = Buffer.alloc(10);
console.log(randomFillSync(buf).toString('hex'));

randomFillSync(buf, 5);
console.log(buf.toString('hex'));

// The above is equivalent to the following:
randomFillSync(buf, 5, 5);
console.log(buf.toString('hex'));
const { randomFillSync } = require('node:crypto');
const { Buffer } = require('node:buffer');

const buf = Buffer.alloc(10);
console.log(randomFillSync(buf).toString('hex'));

randomFillSync(buf, 5);
console.log(buf.toString('hex'));

// The above is equivalent to the following:
randomFillSync(buf, 5, 5);
console.log(buf.toString('hex'));
javascript

Any ArrayBuffer, TypedArray or DataView instance may be passed as buffer.

import { Buffer } from 'node:buffer';
const { randomFillSync } = await import('node:crypto');

const a = new Uint32Array(10);
console.log(Buffer.from(randomFillSync(a).buffer,
                        a.byteOffset, a.byteLength).toString('hex'));

const b = new DataView(new ArrayBuffer(10));
console.log(Buffer.from(randomFillSync(b).buffer,
                        b.byteOffset, b.byteLength).toString('hex'));

const c = new ArrayBuffer(10);
console.log(Buffer.from(randomFillSync(c)).toString('hex'));
const { randomFillSync } = require('node:crypto');
const { Buffer } = require('node:buffer');

const a = new Uint32Array(10);
console.log(Buffer.from(randomFillSync(a).buffer,
                        a.byteOffset, a.byteLength).toString('hex'));

const b = new DataView(new ArrayBuffer(10));
console.log(Buffer.from(randomFillSync(b).buffer,
                        b.byteOffset, b.byteLength).toString('hex'));

const c = new ArrayBuffer(10);
console.log(Buffer.from(randomFillSync(c)).toString('hex'));
javascript

crypto.randomInt([min, ]max[, callback])#

  • min <integer> Start of random range (inclusive). Default: 0.
  • max <integer> End of random range (exclusive).
  • callback <Function> function(err, n) {}.

Return a random integer n such that min <= n < max. This implementation avoids modulo bias.

The range (max - min) must be less than 248. min and max must be safe integers.

If the callback function is not provided, the random integer is generated synchronously.

// Asynchronous
const {
  randomInt,
} = await import('node:crypto');

randomInt(3, (err, n) => {
  if (err) throw err;
  console.log(`Random number chosen from (0, 1, 2): ${n}`);
});
// Asynchronous
const {
  randomInt,
} = require('node:crypto');

randomInt(3, (err, n) => {
  if (err) throw err;
  console.log(`Random number chosen from (0, 1, 2): ${n}`);
});
javascript
// Synchronous
const {
  randomInt,
} = await import('node:crypto');

const n = randomInt(3);
console.log(`Random number chosen from (0, 1, 2): ${n}`);
// Synchronous
const {
  randomInt,
} = require('node:crypto');

const n = randomInt(3);
console.log(`Random number chosen from (0, 1, 2): ${n}`);
javascript
// With `min` argument
const {
  randomInt,
} = await import('node:crypto');

const n = randomInt(1, 7);
console.log(`The dice rolled: ${n}`);
// With `min` argument
const {
  randomInt,
} = require('node:crypto');

const n = randomInt(1, 7);
console.log(`The dice rolled: ${n}`);
javascript

crypto.randomUUID([options])#

  • options <Object>
    • disableEntropyCache <boolean> By default, to improve performance, Node.js generates and caches enough random data to generate up to 128 random UUIDs. To generate a UUID without using the cache, set disableEntropyCache to true. Default: false.
  • Returns: <string>

Generates a random RFC 4122 version 4 UUID. The UUID is generated using a cryptographic pseudorandom number generator.

crypto.randomUUIDv7([options])#

  • options <Object>
    • disableEntropyCache <boolean> By default, to improve performance, Node.js generates and caches enough random data to generate up to 128 random UUIDs. To generate a UUID without using the cache, set disableEntropyCache to true. Default: false.
  • Returns: <string>

Generates a random RFC 9562 version 7 UUID. The UUID contains a millisecond precision Unix timestamp in the most significant 48 bits, followed by cryptographically secure random bits for the remaining fields, making it suitable for use as a database key with time-based sorting. The embedded timestamp relies on a non-monotonic clock and is not guaranteed to be strictly increasing.

crypto.scrypt(password, salt, keylen[, options], callback)#

Provides an asynchronous scrypt implementation. Scrypt is a password-based key derivation function that is designed to be expensive computationally and memory-wise in order to make brute-force attacks unrewarding.

The salt should be as unique as possible. It is recommended that a salt is random and at least 16 bytes long. See NIST SP 800-132 for details.

When passing strings for password or salt, please consider caveats when using strings as inputs to cryptographic APIs.

The callback function is called with two arguments: err and derivedKey. err is an exception object when key derivation fails, otherwise err is null. derivedKey is passed to the callback as a Buffer.

An exception is thrown when any of the input arguments specify invalid values or types.

const {
  scrypt,
} = await import('node:crypto');

// Using the factory defaults.
scrypt('password', 'salt', 64, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...08d59ae'
});
// Using a custom N parameter. Must be a power of two.
scrypt('password', 'salt', 64, { N: 1024 }, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...aa39b34'
});
const {
  scrypt,
} = require('node:crypto');

// Using the factory defaults.
scrypt('password', 'salt', 64, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...08d59ae'
});
// Using a custom N parameter. Must be a power of two.
scrypt('password', 'salt', 64, { N: 1024 }, (err, derivedKey) => {
  if (err) throw err;
  console.log(derivedKey.toString('hex'));  // '3745e48...aa39b34'
});
javascript

crypto.scryptSync(password, salt, keylen[, options])#

Provides a synchronous scrypt implementation. Scrypt is a password-based key derivation function that is designed to be expensive computationally and memory-wise in order to make brute-force attacks unrewarding.

The salt should be as unique as possible. It is recommended that a salt is random and at least 16 bytes long. See NIST SP 800-132 for details.

When passing strings for password or salt, please consider caveats when using strings as inputs to cryptographic APIs.

An exception is thrown when key derivation fails, otherwise the derived key is returned as a Buffer.

An exception is thrown when any of the input arguments specify invalid values or types.

const {
  scryptSync,
} = await import('node:crypto');
// Using the factory defaults.

const key1 = scryptSync('password', 'salt', 64);
console.log(key1.toString('hex'));  // '3745e48...08d59ae'
// Using a custom N parameter. Must be a power of two.
const key2 = scryptSync('password', 'salt', 64, { N: 1024 });
console.log(key2.toString('hex'));  // '3745e48...aa39b34'
const {
  scryptSync,
} = require('node:crypto');
// Using the factory defaults.

const key1 = scryptSync('password', 'salt', 64);
console.log(key1.toString('hex'));  // '3745e48...08d59ae'
// Using a custom N parameter. Must be a power of two.
const key2 = scryptSync('password', 'salt', 64, { N: 1024 });
console.log(key2.toString('hex'));  // '3745e48...aa39b34'
javascript

crypto.secureHeapUsed()#

  • Returns: <Object>
    • total <number> The total allocated secure heap size as specified using the --secure-heap=n command-line flag.
    • min <number> The minimum allocation from the secure heap as specified using the --secure-heap-min command-line flag.
    • used <number> The total number of bytes currently allocated from the secure heap.
    • utilization <number> The calculated ratio of used to total allocated bytes.

crypto.setEngine(engine[, flags])#

Stability: 0 - Deprecated

Load and set the engine for some or all OpenSSL functions (selected by flags). Use of this API is deprecated because custom engine support has been deprecated since OpenSSL 3.

engine could be either an id or a path to the engine's shared library.

The optional flags argument uses ENGINE_METHOD_ALL by default. The flags is a bit field taking one of or a mix of the following flags (defined in crypto.constants):

  • crypto.constants.ENGINE_METHOD_RSA
  • crypto.constants.ENGINE_METHOD_DSA
  • crypto.constants.ENGINE_METHOD_DH
  • crypto.constants.ENGINE_METHOD_RAND
  • crypto.constants.ENGINE_METHOD_EC
  • crypto.constants.ENGINE_METHOD_CIPHERS
  • crypto.constants.ENGINE_METHOD_DIGESTS
  • crypto.constants.ENGINE_METHOD_PKEY_METHS
  • crypto.constants.ENGINE_METHOD_PKEY_ASN1_METHS
  • crypto.constants.ENGINE_METHOD_ALL
  • crypto.constants.ENGINE_METHOD_NONE

crypto.setFips(bool)#

  • bool <boolean> true to enable FIPS mode, false to disable it.

Changes FIPS mode. With OpenSSL 3, this only adds or removes fips=yes in the default property query. It does not install, load, initialize, or validate a FIPS provider. For a usable FIPS configuration, install the provider and configure OpenSSL to load it when Node.js starts, as described in FIPS mode.

If no loaded provider supplies a requested cryptographic implementation matching fips=yes, the call can still succeed and crypto.getFips() can still return 1, but fetching that implementation fails. Affected node:crypto operations typically fail with ERR_OSSL_EVP_UNSUPPORTED. Operations that do not require a new fetch, including those using previously fetched implementations or initialized operation contexts, may still succeed. Call this method during application initialization, before application code uses other OpenSSL-backed APIs.

This method only affects subsequent algorithm fetches. Node.js initializes some OpenSSL state before application code runs. When the property query must be active from process startup, set default_properties = fips=yes in the OpenSSL configuration or use --enable-fips or --force-fips. The command-line flags additionally require a configured provider named fips to initialize and pass its self-test; Node.js fails to start otherwise.

Throws an error if OpenSSL cannot change the state. FIPS mode cannot be disabled when Node.js was started with --force-fips. With OpenSSL 1.1.1, enabling FIPS mode requires a FIPS-capable OpenSSL build.

crypto.sign(algorithm, data, key[, callback])#

Calculates and returns the signature for data using the given private key and algorithm. If algorithm is null or undefined, then the algorithm is dependent upon the key type.

algorithm is required to be null or undefined for Ed25519, Ed448, and ML-DSA.

crypto.getHashes() lists algorithms available to the hashing APIs, but the key type and signature scheme determine whether a listed digest can be used for signing.

If key is not a KeyObject, this function behaves as if key had been passed to crypto.createPrivateKey(). When key is a string, ArrayBuffer, Buffer, TypedArray, or DataView, it must contain PEM-encoded key material. If it is an object, the following additional properties can be passed:

  • dsaEncoding <string> For DSA and ECDSA, this option specifies the format of the generated signature. It can be one of the following:

    • 'der' (default): DER-encoded ASN.1 signature structure encoding (r, s).
    • 'ieee-p1363': Signature format r || s as proposed in IEEE-P1363.
  • padding <integer> Optional padding value for RSA, one of the following:

    • crypto.constants.RSA_PKCS1_PADDING (default)
    • crypto.constants.RSA_PKCS1_PSS_PADDING

    RSA_PKCS1_PSS_PADDING will use MGF1 with the same hash function used to sign the message as specified in section 3.1 of RFC 4055.

  • saltLength <integer> Salt length for when padding is RSA_PKCS1_PSS_PADDING. The special value crypto.constants.RSA_PSS_SALTLEN_DIGEST sets the salt length to the digest size, crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN (default) sets it to the maximum permissible value.

  • context <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> For Ed255193 (using Ed25519ctx from RFC 8032), Ed448, ML-DSA, and SLH-DSA, this option specifies the optional context to differentiate signatures generated for different purposes with the same key.

If the callback function is provided this function uses libuv's threadpool.

crypto.subtle#

A convenient alias for crypto.webcrypto.subtle.

crypto.timingSafeEqual(a, b)#

This function compares the underlying bytes that represent the given ArrayBuffer, TypedArray, or DataView instances using a constant-time algorithm.

This function does not leak timing information that would allow an attacker to guess one of the values. This is suitable for comparing HMAC digests or secret values like authentication cookies or capability urls.

a and b must both be Buffers, TypedArrays, or DataViews, and they must have the same byte length. An error is thrown if a and b have different byte lengths.

If at least one of a and b is a TypedArray with more than one byte per entry, such as Uint16Array, the result will be computed using the platform byte order.

When both of the inputs are Float32Arrays or Float64Arrays, this function might return unexpected results due to IEEE 754 encoding of floating-point numbers. In particular, neither x === y nor Object.is(x, y) implies that the byte representations of two floating-point numbers x and y are equal.

Use of crypto.timingSafeEqual does not guarantee that the surrounding code is timing-safe. Care should be taken to ensure that the surrounding code does not introduce timing vulnerabilities.

crypto.verify(algorithm, data, key, signature[, callback])#

Verifies the given signature for data using the given key and algorithm. If algorithm is null or undefined, then the algorithm is dependent upon the key type.

algorithm is required to be null or undefined for Ed25519, Ed448, and ML-DSA.

crypto.getHashes() lists algorithms available to the hashing APIs, but the key type and signature scheme determine whether a listed digest can be used for verification.

If key is not a KeyObject, this function behaves as if key had been passed to crypto.createPublicKey(). When key is a string, ArrayBuffer, Buffer, TypedArray, or DataView, it must contain PEM-encoded key material. If it is an object, the following additional properties can be passed:

  • dsaEncoding <string> For DSA and ECDSA, this option specifies the format of the signature. It can be one of the following:

    • 'der' (default): DER-encoded ASN.1 signature structure encoding (r, s).
    • 'ieee-p1363': Signature format r || s as proposed in IEEE-P1363.
  • padding <integer> Optional padding value for RSA, one of the following:

    • crypto.constants.RSA_PKCS1_PADDING (default)
    • crypto.constants.RSA_PKCS1_PSS_PADDING

    RSA_PKCS1_PSS_PADDING will use MGF1 with the same hash function used to sign the message as specified in section 3.1 of RFC 4055.

  • saltLength <integer> Salt length for when padding is RSA_PKCS1_PSS_PADDING. The special value crypto.constants.RSA_PSS_SALTLEN_DIGEST sets the salt length to the digest size, crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN (default) sets it to the maximum permissible value.

  • context <ArrayBuffer> | <Buffer> | <TypedArray> | <DataView> For Ed255193 (using Ed25519ctx from RFC 8032), Ed448, ML-DSA, and SLH-DSA, this option specifies the optional context to differentiate signatures generated for different purposes with the same key.

The signature argument is the previously calculated signature for the data.

Because public keys can be derived from private keys, a private key or a public key may be passed for key.

If the callback function is provided this function uses libuv's threadpool.

crypto.webcrypto#

Type: <Crypto> An implementation of the Web Crypto API standard.

See the Web Crypto API documentation for details.

Notes#

Using strings as inputs to cryptographic APIs#

For historical reasons, many cryptographic APIs provided by Node.js accept strings as inputs where the underlying cryptographic algorithm works on byte sequences. These instances include plaintexts, ciphertexts, symmetric keys, initialization vectors, passphrases, salts, authentication tags, and additional authenticated data.

When passing strings to cryptographic APIs, consider the following factors.

  • Not all byte sequences are valid UTF-8 strings. Therefore, when a byte sequence of length n is derived from a string, its entropy is generally lower than the entropy of a random or pseudorandom n byte sequence. For example, no UTF-8 string will result in the byte sequence c0 af. Secret keys should almost exclusively be random or pseudorandom byte sequences.

  • Similarly, when converting random or pseudorandom byte sequences to UTF-8 strings, subsequences that do not represent valid code points may be replaced by the Unicode replacement character (U+FFFD). The byte representation of the resulting Unicode string may, therefore, not be equal to the byte sequence that the string was created from.

    const original = [0xc0, 0xaf];
    const bytesAsString = Buffer.from(original).toString('utf8');
    const stringAsBytes = Buffer.from(bytesAsString, 'utf8');
    console.log(stringAsBytes);
    // Prints '<Buffer ef bf bd ef bf bd>'.
    
    js

    The outputs of ciphers, hash functions, signature algorithms, and key derivation functions are pseudorandom byte sequences and should not be used as Unicode strings.

  • When strings are obtained from user input, some Unicode characters can be represented in multiple equivalent ways that result in different byte sequences. For example, when passing a user passphrase to a key derivation function, such as PBKDF2 or scrypt, the result of the key derivation function depends on whether the string uses composed or decomposed characters. Node.js does not normalize character representations. Developers should consider using String.prototype.normalize() on user inputs before passing them to cryptographic APIs.

Legacy streams API (prior to Node.js 0.10)#

The Crypto module was added to Node.js before there was the concept of a unified Stream API, and before there were Buffer objects for handling binary data. As such, many crypto classes have methods not typically found on other Node.js classes that implement the streams API (e.g. update(), final(), or digest()). Also, many methods accepted and returned 'latin1' encoded strings by default rather than Buffers. This default was changed in Node.js 0.9.3 to use Buffer objects by default instead.

Support for weak or compromised algorithms#

The node:crypto module still supports some algorithms which are already compromised and are not recommended for use. The API also allows the use of ciphers and hashes with a small key size that are too weak for safe use.

Users should take full responsibility for selecting the crypto algorithm and key size according to their security requirements.

Based on the recommendations of NIST SP 800-131A:

  • MD5 and SHA-1 are no longer acceptable where collision resistance is required such as digital signatures.
  • The key used with RSA, DSA, and DH algorithms is recommended to have at least 2048 bits and that of the curve of ECDSA and ECDH at least 224 bits, to be safe to use for several years.
  • The DH groups of modp1, modp2 and modp5 have a key size smaller than 2048 bits and are not recommended.

See the reference for other recommendations and details.

Some algorithms that have known weaknesses and are of little relevance in practice are only available through the legacy provider, which is not enabled by default.

CCM mode#

CCM is one of the supported AEAD algorithms. Applications which use this mode must adhere to certain restrictions when using the cipher API:

  • The authentication tag length must be specified during cipher creation by setting the authTagLength option and must be one of 4, 6, 8, 10, 12, 14 or 16 bytes.
  • The length of the initialization vector (nonce) N must be between 7 and 13 bytes (7 ≤ N ≤ 13).
  • The length of the plaintext is limited to 2 ** (8 * (15 - N)) bytes.
  • When decrypting, the authentication tag must be set via setAuthTag() before calling update(). Otherwise, decryption will fail and final() will throw an error in compliance with section 2.6 of RFC 3610.
  • Using stream methods such as write(data), end(data) or pipe() in CCM mode might fail as CCM cannot handle more than one chunk of data per instance.
  • When passing additional authenticated data (AAD), the length of the actual message in bytes must be passed to setAAD() via the plaintextLength option. Many crypto libraries include the authentication tag in the ciphertext, which means that they produce ciphertexts of the length plaintextLength + authTagLength. Node.js does not include the authentication tag, so the ciphertext length is always plaintextLength. This is not necessary if no AAD is used.
  • As CCM processes the whole message at once, update() must be called exactly once.
  • Even though calling update() is sufficient to encrypt/decrypt the message, applications must call final() to compute or verify the authentication tag.
import { Buffer } from 'node:buffer';
const {
  createCipheriv,
  createDecipheriv,
  randomBytes,
} = await import('node:crypto');

const key = 'keykeykeykeykeykeykeykey';
const nonce = randomBytes(12);

const aad = Buffer.from('0123456789', 'hex');

const cipher = createCipheriv('aes-192-ccm', key, nonce, {
  authTagLength: 16,
});
const plaintext = 'Hello world';
cipher.setAAD(aad, {
  plaintextLength: Buffer.byteLength(plaintext),
});
const ciphertext = cipher.update(plaintext, 'utf8');
cipher.final();
const tag = cipher.getAuthTag();

// Now transmit { ciphertext, nonce, tag }.

const decipher = createDecipheriv('aes-192-ccm', key, nonce, {
  authTagLength: 16,
});
decipher.setAuthTag(tag);
decipher.setAAD(aad, {
  plaintextLength: ciphertext.length,
});
const receivedPlaintext = decipher.update(ciphertext, null, 'utf8');

try {
  decipher.final();
} catch (err) {
  throw new Error('Authentication failed!', { cause: err });
}

console.log(receivedPlaintext);
const { Buffer } = require('node:buffer');
const {
  createCipheriv,
  createDecipheriv,
  randomBytes,
} = require('node:crypto');

const key = 'keykeykeykeykeykeykeykey';
const nonce = randomBytes(12);

const aad = Buffer.from('0123456789', 'hex');

const cipher = createCipheriv('aes-192-ccm', key, nonce, {
  authTagLength: 16,
});
const plaintext = 'Hello world';
cipher.setAAD(aad, {
  plaintextLength: Buffer.byteLength(plaintext),
});
const ciphertext = cipher.update(plaintext, 'utf8');
cipher.final();
const tag = cipher.getAuthTag();

// Now transmit { ciphertext, nonce, tag }.

const decipher = createDecipheriv('aes-192-ccm', key, nonce, {
  authTagLength: 16,
});
decipher.setAuthTag(tag);
decipher.setAAD(aad, {
  plaintextLength: ciphertext.length,
});
const receivedPlaintext = decipher.update(ciphertext, null, 'utf8');

try {
  decipher.final();
} catch (err) {
  throw new Error('Authentication failed!', { cause: err });
}

console.log(receivedPlaintext);
javascript

CBC-CTS mode#

For CBC ciphertext stealing (CBC-CTS) ciphers, the ctsMode option to crypto.createCipheriv() or crypto.createDecipheriv() selects the variant:

  • 'CS1' is the default. For block-aligned input, its output is the same as CBC mode.
  • 'CS2' is also the same as CBC for block-aligned input. For input with a partial final block, it swaps the final full and partial ciphertext blocks relative to CS1.
  • 'CS3' is the Kerberos 5 variant. It uses the CS2 ordering for a partial final block and swaps the final two ciphertext blocks even for block-aligned input.

Encryption and decryption must use the same variant. The option is available only with CBC-CTS provider ciphers on OpenSSL 3.0 or later.

Applications which use this mode must adhere to these restrictions:

  • The plaintext or ciphertext must be at least one block long.
  • The ciphertext has the same length as the plaintext.
  • cipher.update() or decipher.update() must be called exactly once with all input data. Stream methods such as write(data), end(data), or pipe() may fail because CBC-CTS does not accept multiple data updates.
  • cipher.final() or decipher.final() must still be called to complete the operation.
  • crypto.getCipherInfo() reports the base mode as 'cbc'.

XTS mode#

XTS ciphers operate on independently tweakable data units. A Cipheriv or Decipheriv instance represents one complete data unit, and its iv argument provides the 16-byte tweak. Use a new instance with the appropriate positional tweak for each different logical data unit.

Applications which use XTS mode must adhere to these restrictions:

  • The plaintext or ciphertext must be at least one 16-byte block. Its length does not have to be a multiple of 16 bytes because XTS uses ciphertext stealing for a final partial block.
  • The ciphertext has the same length as the plaintext.
  • cipher.update() or decipher.update() must be called exactly once with all input data. Stream methods such as write(data), end(data), or pipe() may fail because XTS does not accept multiple data updates.
  • cipher.final() or decipher.final() must still be called to complete the operation.

For sm4-xts, the xtsStandard option to crypto.createCipheriv() or crypto.createDecipheriv() selects either the default 'GB' variant from GB/T 17964-2021 or the 'IEEE' variant from IEEE Std 1619-2007. Encryption and decryption must use the same variant. The option is available only for sm4-xts; it does not apply to AES-XTS ciphers. OpenSSL's default provider supports sm4-xts in OpenSSL 3.2 or later.

AES key wrap modes#

AES key wrap (AES-WRAP) and AES key wrap with padding (AES-WRAP-PAD) ciphers operate on a complete key-data value rather than on an incremental byte stream. The inverse-transform variants have the same processing restrictions.

Applications which use an AES key wrap cipher must adhere to these restrictions:

  • cipher.update() or decipher.update() must be called exactly once with the complete, non-empty input value.
  • Do not use AES key wrap ciphers as generic stream.Transform streams. Methods such as write(data), end(data), and pipe() can split one value across multiple updates, with each update being treated as a separate wrap or unwrap operation.
  • cipher.final() or decipher.final() must still be called to complete the operation.

SIV and GCM-SIV modes#

SIV2 and GCM-SIV3 are supported AEAD algorithms when supported by OpenSSL. Applications which use these modes must adhere to certain restrictions when using the cipher API:

  • The authentication tag length is fixed at 16 bytes.
  • AES-SIV keys are twice the named AES key size: aes-128-siv requires a 32-byte key, aes-192-siv requires a 48-byte key, and aes-256-siv requires a 64-byte key.
  • AES-SIV ciphers do not use an initialization vector. Pass null or a zero-length iv to crypto.createCipheriv() or crypto.createDecipheriv().
  • AES-SIV and AES-GCM-SIV support zero-length plaintext only with OpenSSL 3.5 or later.
  • AES-SIV does not have a separate nonce or IV parameter. RFC 5297 defines AES-SIV over an ordered list of associated-data inputs. Each setAAD() call supplies one input in that list. If a protocol uses a nonce with AES-SIV, call setAAD(nonce) after the other associated-data inputs and before update(). At most 126 associated-data inputs may be supplied.
  • AES-GCM-SIV ciphers require a 12-byte initialization vector.
  • When decrypting, the authentication tag must be set via setAuthTag() before calling update().
  • Using stream methods such as write(data), end(data) or pipe() might fail as these modes cannot handle more than one chunk of data per instance.
  • As these modes process the whole message at once, update() must be called exactly once.
  • Even though calling update() is sufficient to encrypt/decrypt the message, applications must call final() to compute or verify the authentication tag.

FIPS mode#

Node.js exposes the FIPS support provided by the linked OpenSSL library. Node.js is not itself FIPS validated. Validation belongs to a specific OpenSSL module or provider and only applies when it is deployed according to its security policy. Vendor-provided Node.js or OpenSSL builds can require a different configuration; follow the vendor's documentation for those builds.

With OpenSSL 1.1.1, Node.js must be built against a FIPS-capable OpenSSL library.

With OpenSSL 3, FIPS support uses the provider model described in the OpenSSL FIPS module guide. Using FIPS-approved implementations requires:

  • A correctly installed OpenSSL 3 FIPS provider.
  • An OpenSSL 3 FIPS module configuration file.
  • The FIPS provider to be loaded into the OpenSSL library context used by Node.js, normally by activating it in an OpenSSL configuration file when Node.js starts.
  • The default property query to include fips=yes when cryptographic implementations are fetched. This can be set from process startup by the OpenSSL configuration, --enable-fips, or --force-fips, or for subsequent fetches by crypto.setFips(true).

An example OpenSSL 3 configuration file looks like this:

nodejs_conf = nodejs_init
config_diagnostics = 1

.include /<absolute path>/fipsmodule.cnf

[nodejs_init]
providers = provider_sect
alg_section = algorithm_sect

[provider_sect]
# The fips section name should match the section name inside the
# included fipsmodule.cnf.
fips = fips_sect
base = base_sect

[base_sect]
activate = 1

[algorithm_sect]
default_properties = fips=yes
text

The fipsmodule.cnf file is generated as part of the FIPS provider installation and contains module integrity and self-test information. The exact command and arguments are installation-specific; see OpenSSL FIPS configuration and the OpenSSL FIPS module guide. The installation uses openssl fipsinstall.

The example activates the provider and enables the fips=yes property query when Node.js starts. To activate the provider at startup but enable the property query later with crypto.setFips(true), omit alg_section = algorithm_sect and the [algorithm_sect] block. The provider must still be loaded; when using this startup configuration, keep its activation enabled. crypto.setFips(true) should be called before application code uses other OpenSSL-backed APIs. It is not equivalent to enabling the property query from process startup because Node.js initializes some OpenSSL state before application code runs. Use the example as written, --enable-fips, or --force-fips when the property query must be active from process startup.

config_diagnostics causes configuration errors to prevent startup instead of being ignored. The base provider supplies non-cryptographic supporting algorithms, such as encoders and decoders, that are commonly needed alongside the FIPS provider. default_properties = fips=yes restricts OpenSSL's default algorithm selection to implementations that match fips=yes.

Set OPENSSL_CONF to the OpenSSL configuration file. For a dynamically loaded provider, OPENSSL_MODULES can set the directory containing the provider module. For example:

export OPENSSL_CONF=/<path to configuration file>/nodejs.cnf
export OPENSSL_MODULES=/<path to openssl lib>/ossl-modules
bash

The --openssl-config command-line option selects the configuration file and takes precedence over OPENSSL_CONF. If neither is set, OpenSSL's default configuration file is used.

By default, Node.js reads the nodejs_conf section instead of OpenSSL's usual openssl_conf section. Use --openssl-shared-config to read openssl_conf, or build Node.js with ./configure --openssl-conf-name=<name> to change the default section name.

On OpenSSL 3, the configuration above enables the fips=yes property query at startup. The following controls are also available:

  • --enable-fips and --force-fips enable the property query and additionally require the configured provider named fips to initialize and pass its self-test. Node.js exits if that check fails. --force-fips also prevents FIPS mode from being disabled from script code. With --force-fips=strict, Node.js also rejects non-approved operations reported through the OpenSSL FIPS indicator callback.
  • crypto.setFips() changes the FIPS/property-query state. On OpenSSL 3, it does not install, load, initialize, or validate a provider. Implementations fetched before the call are not changed.
  • crypto.getFips() reports the FIPS/property-query state. On OpenSSL 3, a return value of 1 does not prove that a FIPS provider is loaded or validated.
  • With --enable-fips-indicator-events, the 'crypto.fips.indicator' diagnostics channel reports non-approved operations permitted by an OpenSSL 3.4 or later FIPS provider configured for backwards compatibility.

With OpenSSL 1.1.1, these controls use the library's FIPS mode support and require a FIPS-capable OpenSSL build.

Only algorithms available under the active FIPS settings can be used. With OpenSSL 3, if no loaded provider supplies a requested cryptographic implementation matching fips=yes, fetching it fails, typically with ERR_OSSL_EVP_UNSUPPORTED. The same error can occur for algorithms that Node.js supports when FIPS mode is disabled but that are unavailable under the active FIPS settings.

OpenSSL documents that the same FIPS provider cannot be used by multiple copies of libcrypto in one process. This can affect native addons that load another copy of libcrypto; OpenSSL's documented workaround is to use a separate copy of the provider for each libcrypto instance. See OpenSSL FIPS provider limitations.

Crypto constants#

The following constants exported by crypto.constants apply to various uses of the node:crypto, node:tls, and node:https modules and are generally specific to OpenSSL.

OpenSSL options#

See the list of SSL OP Flags for details.

Constant Description
SSL_OP_ALL Applies multiple bug workarounds within OpenSSL. See https://www.openssl.org/docs/man3.0/man3/SSL_CTX_set_options.html for detail.
SSL_OP_ALLOW_NO_DHE_KEX Instructs OpenSSL to allow a non-[EC]DHE-based key exchange mode for TLS v1.3
SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION Allows legacy insecure renegotiation between OpenSSL and unpatched clients or servers. See https://www.openssl.org/docs/man3.0/man3/SSL_CTX_set_options.html.
SSL_OP_CIPHER_SERVER_PREFERENCE Attempts to use the server's preferences instead of the client's when selecting a cipher. Behavior depends on protocol version. See https://www.openssl.org/docs/man3.0/man3/SSL_CTX_set_options.html.
SSL_OP_CISCO_ANYCONNECT Instructs OpenSSL to use Cisco's version identifier of DTLS_BAD_VER.
SSL_OP_COOKIE_EXCHANGE Instructs OpenSSL to turn on cookie exchange.
SSL_OP_CRYPTOPRO_TLSEXT_BUG Instructs OpenSSL to add server-hello extension from an early version of the cryptopro draft.
SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS Instructs OpenSSL to disable an SSL 3.0/TLS 1.0 vulnerability workaround added in OpenSSL 0.9.6d.
SSL_OP_LEGACY_SERVER_CONNECT Allows initial connection to servers that do not support RI.
SSL_OP_NO_COMPRESSION Instructs OpenSSL to disable support for SSL/TLS compression.
SSL_OP_NO_ENCRYPT_THEN_MAC Instructs OpenSSL to disable encrypt-then-MAC.
SSL_OP_NO_QUERY_MTU
SSL_OP_NO_RENEGOTIATION Instructs OpenSSL to disable renegotiation.
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION Instructs OpenSSL to always start a new session when performing renegotiation.
SSL_OP_NO_SSLv2 Instructs OpenSSL to turn off SSL v2
SSL_OP_NO_SSLv3 Instructs OpenSSL to turn off SSL v3
SSL_OP_NO_TICKET Instructs OpenSSL to disable use of RFC4507bis tickets.
SSL_OP_NO_TLSv1 Instructs OpenSSL to turn off TLS v1
SSL_OP_NO_TLSv1_1 Instructs OpenSSL to turn off TLS v1.1
SSL_OP_NO_TLSv1_2 Instructs OpenSSL to turn off TLS v1.2
SSL_OP_NO_TLSv1_3 Instructs OpenSSL to turn off TLS v1.3
SSL_OP_PRIORITIZE_CHACHA Instructs OpenSSL server to prioritize ChaCha20-Poly1305 when the client does. This option has no effect if SSL_OP_CIPHER_SERVER_PREFERENCE is not enabled.
SSL_OP_TLS_ROLLBACK_BUG Instructs OpenSSL to disable version rollback attack detection.

OpenSSL engine constants#

Constant Description
ENGINE_METHOD_RSA Limit engine usage to RSA
ENGINE_METHOD_DSA Limit engine usage to DSA
ENGINE_METHOD_DH Limit engine usage to DH
ENGINE_METHOD_RAND Limit engine usage to RAND
ENGINE_METHOD_EC Limit engine usage to EC
ENGINE_METHOD_CIPHERS Limit engine usage to CIPHERS
ENGINE_METHOD_DIGESTS Limit engine usage to DIGESTS
ENGINE_METHOD_PKEY_METHS Limit engine usage to PKEY_METHS
ENGINE_METHOD_PKEY_ASN1_METHS Limit engine usage to PKEY_ASN1_METHS
ENGINE_METHOD_ALL
ENGINE_METHOD_NONE

Other OpenSSL constants#

Constant Description
DH_CHECK_P_NOT_SAFE_PRIME
DH_CHECK_P_NOT_PRIME
DH_UNABLE_TO_CHECK_GENERATOR
DH_NOT_SUITABLE_GENERATOR
RSA_PKCS1_PADDING
RSA_SSLV23_PADDING
RSA_NO_PADDING
RSA_PKCS1_OAEP_PADDING
RSA_X931_PADDING
RSA_PKCS1_PSS_PADDING
RSA_PSS_SALTLEN_DIGEST Sets the salt length for RSA_PKCS1_PSS_PADDING to the digest size when signing or verifying.
RSA_PSS_SALTLEN_MAX_SIGN Sets the salt length for RSA_PKCS1_PSS_PADDING to the maximum permissible value when signing data.
RSA_PSS_SALTLEN_AUTO Causes the salt length for RSA_PKCS1_PSS_PADDING to be determined automatically when verifying a signature.
POINT_CONVERSION_COMPRESSED
POINT_CONVERSION_UNCOMPRESSED
POINT_CONVERSION_HYBRID

Node.js crypto constants#

Constant Description
defaultCoreCipherList Specifies the built-in default cipher list used by Node.js.
defaultCipherList Specifies the active default cipher list used by the current Node.js process.

Footnotes

  1. Requires OpenSSL >= 3.5 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16 ↩17 ↩18 ↩19 ↩20 ↩21 ↩22 ↩23 ↩24

  2. Requires OpenSSL >= 3.0 ↩ ↩2 ↩3

  3. Requires OpenSSL >= 3.2 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9

Debugger#

Stability: 2 - Stable

Node.js includes a command-line debugging utility. The Node.js debugger client is not a full-featured debugger, but simple stepping and inspection are possible.

The debugger supports two modes of operation: interactive mode and non-interactive probe mode.

Interactive mode#

$ node inspect [--port=<port>] [<node-option> ...] [<script> [<script-args>] | <host>:<port> | -p <pid>]
console

To use it, start Node.js with the inspect argument followed by the path to the script to debug.

$ node inspect myscript.js
< Debugger listening on ws://127.0.0.1:9229/621111f9-ffcb-4e82-b718-48a145fa5db8
< For help, see: https://nodejs.org/learn/getting-started/debugging
<
connecting to 127.0.0.1:9229 ... ok
< Debugger attached.
<
 ok
Break on start in myscript.js:2
  1 // myscript.js
> 2 global.x = 5;
  3 setTimeout(() => {
  4   debugger;
debug>
console

The debugger automatically breaks on the first executable line. To instead run until the first breakpoint (specified by a debugger statement), set the NODE_INSPECT_RESUME_ON_START environment variable to 1.

$ cat myscript.js
// myscript.js
global.x = 5;
setTimeout(() => {
  debugger;
  console.log('world');
}, 1000);
console.log('hello');
$ NODE_INSPECT_RESUME_ON_START=1 node inspect myscript.js
< Debugger listening on ws://127.0.0.1:9229/f1ed133e-7876-495b-83ae-c32c6fc319c2
< For help, see: https://nodejs.org/learn/getting-started/debugging
<
connecting to 127.0.0.1:9229 ... ok
< Debugger attached.
<
< hello
<
break in myscript.js:4
  2 global.x = 5;
  3 setTimeout(() => {
> 4   debugger;
  5   console.log('world');
  6 }, 1000);
debug> next
break in myscript.js:5
  3 setTimeout(() => {
  4   debugger;
> 5   console.log('world');
  6 }, 1000);
  7 console.log('hello');
debug> repl
Press Ctrl+C to leave debug repl
> x
5
> 2 + 2
4
debug> next
< world
<
break in myscript.js:6
  4   debugger;
  5   console.log('world');
> 6 }, 1000);
  7 console.log('hello');
  8
debug> .exit
$
console

The repl command allows code to be evaluated remotely. The next command steps to the next line. Type help to see what other commands are available.

Pressing enter without typing a command will repeat the previous debugger command.

Watchers#

It is possible to watch expression and variable values while debugging. On every breakpoint, each expression from the watchers list will be evaluated in the current context and displayed immediately before the breakpoint's source code listing.

To begin watching an expression, type watch('my_expression'). The command watchers will print the active watchers. To remove a watcher, type unwatch('my_expression').

Command reference#

Stepping#

  • cont, c: Continue execution
  • next, n: Step next
  • step, s: Step in
  • out, o: Step out
  • pause: Pause running code (like pause button in Developer Tools)
Breakpoints#
  • setBreakpoint(), sb(): Set breakpoint on current line
  • setBreakpoint(line), sb(line): Set breakpoint on specific line
  • setBreakpoint('fn()'), sb(...): Set breakpoint on a first statement in function's body
  • setBreakpoint('script.js', 1), sb(...): Set breakpoint on first line of script.js
  • setBreakpoint('script.js', 1, 'num < 4'), sb(...): Set conditional breakpoint on first line of script.js that only breaks when num < 4 evaluates to true
  • clearBreakpoint('script.js', 1), cb(...): Clear breakpoint in script.js on line 1

It is also possible to set a breakpoint in a file (module) that is not loaded yet:

$ node inspect main.js
< Debugger listening on ws://127.0.0.1:9229/48a5b28a-550c-471b-b5e1-d13dd7165df9
< For help, see: https://nodejs.org/learn/getting-started/debugging
<
connecting to 127.0.0.1:9229 ... ok
< Debugger attached.
<
Break on start in main.js:1
> 1 const mod = require('./mod.js');
  2 mod.hello();
  3 mod.hello();
debug> setBreakpoint('mod.js', 22)
Warning: script 'mod.js' was not loaded yet.
debug> c
break in mod.js:22
 20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
 21
>22 exports.hello = function() {
 23   return 'hello from module';
 24 };
debug>
console

It is also possible to set a conditional breakpoint that only breaks when a given expression evaluates to true:

$ node inspect main.js
< Debugger listening on ws://127.0.0.1:9229/ce24daa8-3816-44d4-b8ab-8273c8a66d35
< For help, see: https://nodejs.org/learn/getting-started/debugging
<
connecting to 127.0.0.1:9229 ... ok
< Debugger attached.
Break on start in main.js:7
  5 }
  6
> 7 addOne(10);
  8 addOne(-1);
  9
debug> setBreakpoint('main.js', 4, 'num < 0')
  1 'use strict';
  2
  3 function addOne(num) {
> 4   return num + 1;
  5 }
  6
  7 addOne(10);
  8 addOne(-1);
  9
debug> cont
break in main.js:4
  2
  3 function addOne(num) {
> 4   return num + 1;
  5 }
  6
debug> exec('num')
-1
debug>
console
Information#
  • backtrace, bt: Print backtrace of current execution frame
  • list(5): List scripts source code with 5 line context (5 lines before and after)
  • watch(expr): Add expression to watch list
  • unwatch(expr): Remove expression from watch list
  • unwatch(index): Remove expression at specific index from watch list
  • watchers: List all watchers and their values (automatically listed on each breakpoint)
  • repl: Open debugger's repl for evaluation in debugging script's context
  • exec expr, p expr: Execute an expression in debugging script's context and print its value
  • profile: Start CPU profiling session
  • profileEnd: Stop current CPU profiling session
  • profiles: List all completed CPU profiling sessions
  • profiles[n].save(filepath = 'node.cpuprofile'): Save CPU profiling session to disk as JSON
  • takeHeapSnapshot(filepath = 'node.heapsnapshot'): Take a heap snapshot and save to disk as JSON
Execution control#
  • run: Run script (automatically runs on debugger's start)
  • restart: Restart script
  • kill: Kill script
Various#
  • scripts: List all loaded scripts
  • version: Display V8's version

Probe mode#

Stability: 1 - Experimental

node inspect supports a non-interactive probe mode for inspecting runtime values in an application via the flag --probe.

Currently, probe mode only supports launching a new process from the entry point script specified on the command line.

The probe mode sets one or more source breakpoints, evaluates specified expressions whenever the execution reaches a breakpoint, and prints one final report of all the evaluated expressions when the session ends (either on normal completion, error, or timeout). This allows developers to perform printf-style debugging without having to modify the application code and clean up afterwards. It also supports structured JSON output for tool use.

$ node inspect --probe <file>:<line>[:<col>] --expr <expr> [--cond <expr>] [--max-hit <n>]
              [--probe <file>:<line>[:<col>] --expr <expr> [--cond <expr>] [--max-hit <n>] ...]
              [--json] [--preview] [--timeout=<ms>] [--port=<port>]
              [--] [<node-option> ...] <script> [<script-args> ...]
console
  • --probe <file>:<line>[:<col>]: Source location of the probe. When execution reaches the location, the provided expressions are evaluated and printed in the output. <file> matches the URL suffix of the script to probe. <line> and <col> numbers are 1-based. When <col> is omitted, the probe binds to the first executable column on the line.
  • --expr <expr>: JavaScript expression to evaluate whenever execution reaches the location specified by the preceding --probe. Must immediately follow the --probe it belongs to.
  • --cond <expr>: An optional condition for the probe location. The probe only records a hit when <expr> is truthy at the location. A condition that throws is treated as false.
  • --max-hit <n>: An optional per-probe limit on the number of times the probe can be hit. When not specified, there's no hit limit. When any probe reaches its hit limit, the probing process will detach and report the results. The process being probed will continue to run. If any other probe is never reached by the time the session ends, it will be reported as a missed probe.
  • --timeout=<ms>: A global wall-clock deadline for the entire probe session. The default is 30000. This can be used to probe a long-running application that can be terminated externally.
  • --json: If used, prints a structured JSON report instead of the default text report.
  • --preview: If used, non-primitive values will include CDP property previews for object-like JSON probe values.
  • --port=<port>: Selects the local inspector port where the probing session will listen. Defaults to 0, which requests a random port.
  • -- is optional unless the child needs its own Node.js flags.

Additional rules about the composition of the options:

  • --probe <file>:<line>[:<col>] and --expr <expr> are strict pairs. Each --probe must be followed immediately by exactly one --expr.
  • --cond <expr> and --max-hit <n> are optional modifiers written after the --probe/--expr pair they apply to, each at most once per pair. They may not appear before the first --probe or between a --probe and its matching --expr.
  • --max-hit scopes to the --probe/--expr pair it follows, so pairs sharing a location may set different limits. --cond scopes to the whole location, probes sharing a location must share one condition (or none).
  • --timeout, --json, --preview, and --port are global probe options for the whole probe session. They may appear before or between probe pairs, but not between a --probe and its matching --expr.
  • If additional Node.js execution arguments need to be passed to the child script, -- must be used to separate the probe options from the Node.js options for the child script.

Example:

$ node inspect --probe app.js:10 --expr "user"
               --probe src/utils.js:5:15 --expr "config.options"
               --json --preview -- --no-warnings app.js --arg-for-app=foo
console

Probe output format#

When the probe session ends, the probing process prints a final report of all the probe hits and results.

Consider this script:

// cli.js
let maxRSS = 0;
for (let i = 0; i < 2; i++) {
  const { rss } = process.memoryUsage();
  maxRSS = Math.max(maxRSS, rss);
}
js

Without --json, by default the output is printed in a human-readable text format:

$ node inspect --probe cli.js:5 --expr 'rss' cli.js
Hit 1 at file:///path/to/cli.js:5:3
  rss = 54935552
Hit 2 at file:///path/to/cli.js:5:3
  rss = 55083008
Completed
console

The original <file>:<line>[:<col>] passed to --probe may be resolved to a different location to ensure it's pausable, or it can match multiple loaded scripts, so the actual evaluation location helps disambiguate the results.

Primitive results are printed directly, while objects and arrays use Chrome DevTools Protocol preview data when available. Other non-primitive values fall back to the Chrome DevTools Protocol description string. Expression failures are recorded as [error] ... lines and do not fail the overall session. If richer text formatting is needed, wrap the expression in JSON.stringify(...) or util.inspect(...).

When --json is used, the output shape looks like this:

$ node inspect --json --probe cli.js:5 --expr 'rss' cli.js
{"v":2,"probes":[{"expr":"rss","target":{"suffix":"cli.js","line":5}}],"results":[{"probe":0,"event":"hit","hit":1,"location":{"url":"file:///path/to/cli.js","line":5,"column":3},"result":{"type":"number","value":55443456,"description":"55443456"}},{"probe":0,"event":"hit","hit":2,"location":{"url":"file:///path/to/cli.js","line":5,"column":3},"result":{"type":"number","value":55574528,"description":"55574528"}},{"event":"completed"}]}
console
{
  "v": 2, // Probe JSON schema version.
  "probes": [
    {
      "expr": "rss", // The expression paired with --probe.
      "target": {
        // The user's probe specification. `suffix` is the raw <file> passed
        // to --probe and is matched as a path-separator-anchored suffix
        // against every loaded script's URL. `column` is present only if the
        // user supplied `:col`. The actual evaluation location may differ
        // from the target and will be reported in each hit's `location` field.
        "suffix": "cli.js",
        "line": 5
      }
      // `condition` is present only when the probe was given a --cond expression.
      // `maxHit` is present only when the probe was given a --max-hit limit.
    }
  ],
  "results": [
    {
      "probe": 0, // Index into probes[].
      "event": "hit", // Hit events are recorded in observation order.
      "hit": 1, // 1-based hit count for this probe.
      "location": {
        // The actual location where the execution is paused to evaluate
        // the expression of the probe. This may differ from the probe's
        // target due to pausability adjustments or multiple matches.
        "url": "file:///path/to/cli.js",
        "line": 5,
        "column": 3
      },
      "result": {
        "type": "number",
        "value": 55443456,
        "description": "55443456"
      }
      // If the probe expression throws, fails, or never completes, the entry
      // carries an `error` field instead of `result` with the shape
      // `{ message: string, details?: object }`. The `message` and `details`
      // content is informational only and may change between releases.
    },
    {
      "probe": 0,
      "event": "hit",
      "hit": 2,
      "location": { "url": "file:///path/to/cli.js", "line": 5, "column": 3 },
      "result": {
        "type": "number",
        "value": 55574528,
        "description": "55574528"
      }
    },
    {
      "event": "completed"
      // The final entry is always a terminal event, for example:
      // 1. { "event": "completed" }
      // 2. { "event": "miss", "pending": [0, 1] }
      // 3. {
      //      "event": "timeout",
      //      "pending": [0],
      //      "error": {
      //       "code": "probe_timeout",
      //       "message": "Timed out after 30000ms waiting for probes: app.js:10"
      //      }
      //    }
      // 4. {
      //      "event": "error",
      //      "pending": [0],
      //      "error": {
      //       "code": "probe_target_exit",
      //       "exitCode": 1,
      //       "stderr": "Error: boom",
      //       "message": "Target exited with code 1 before probes: app.js:10"
      //      }
      //    }
      // 5. {
      //      "event": "error",
      //      "pending": [1],
      //      "error": {
      //       "code": "probe_failure",
      //       "probe": 0,
      //       "stderr": "...",
      //       "message": "Target process exited during probe evaluation before probes: app.js:12. If the failure repeats, review the probe expression.",
      //       "details": { "lastCdpMethod": "Debugger.evaluateOnCallFrame" }
      //      }
      //    }
    }
  ]
}
json

Output and exit codes#

Probe mode only prints the final probe report to stdout, and otherwise silences stdout/stderr from the child process. When the probing session ends, the probing process typically exits with code 0 and prints a final report to stdout. If the child process exits with a non-zero code before the probe session ends, or the probe session cannot complete for another reason, the final report records a terminal error event.

When error.code is 'probe_failure' or 'probe_timeout', the probing process exits with a non-zero code, indicating recorded hits may be incomplete. In this case, error.message will contain recovery hints, and error.probe, when present, is an index into the report's probes array that identifies the possible culprit probe on a best-effort basis to help guide debugging.

Invalid arguments and fatal launch or connect failures may cause the probing process to exit with a non-zero code and print an error message to stderr without a final probe report.

Probing multiple expressions at the same execution point#

When multiple --probe/--expr pairs share the same --probe, the expressions will be evaluated on the same pause in the order they appear on the command line.

For each location, there can only be at most one --cond (or none). Multiple --probe/--expr pairs with conflicting conditions at the same location will be rejected at launch time.

// app.js
const x = { x: 42 };       // line 2
const y = { y: 35 };       // line 3
const z = { ...x, ...y };  // line 4
js
$ node inspect --probe app.js:4 --expr 'x' --probe app.js:4 --expr 'y' -- app.js
console

Prints

Hit 1 at file:///path/to/app.js:4:1
  x = {x: 42}
Hit 1 at file:///path/to/app.js:4:1
  y = {y: 35}
Completed
text
$ node inspect --probe app.js:4 --expr 'x' --probe app.js:4 --expr 'y' --json --preview -- app.js
console

Prints

{"v":2,"probes":[{"expr":"x","target":{"suffix":"app.js","line":4}},{"expr":"y","target":{"suffix":"app.js","line":4}}],"results":[{"probe":0,"event":"hit","hit":1,"location":{"url":"file:///path/to/app.js","line":4,"column":1},"result":{"type":"object","description":"Object","preview":{"type":"object","description":"Object","overflow":false,"properties":[{"name":"x","type":"number","value":"42"}]}}},{"probe":1,"event":"hit","hit":1,"location":{"url":"file:///path/to/app.js","line":4,"column":1},"result":{"type":"object","description":"Object","preview":{"type":"object","description":"Object","overflow":false,"properties":[{"name":"y","type":"number","value":"35"}]}}},{"event":"completed"}]}
json

Selecting the probe location#

The expressions are evaluated in the lexical scope of the probe location when execution reaches it. Avoid probing a variable declared by let or const at its declaration site, as this leads to a ReferenceError caused by accessing the variable in its temporal dead zone (TDZ).

// app.js
const x = 42;        // line 2
console.log(x);      // line 3
js
$ node inspect --probe app.js:1 --expr 'x' app.js
Hit 1 at file:///path/to/app.js:1:1
  [error] x = ReferenceError: Cannot access 'x' from debugger
  ...
Completed
console

Instead, probe at a location where the variable is already initialized:

$ node inspect --probe app.js:3 --expr 'x' app.js
Hit 1 at file:///path/to/app.js:3:1
  x = 42
Completed
console

The <file> argument is matched as a path suffix of every loaded script URL, anchored on a path separator. Passing only a basename matches every loaded script with that basename, similar to how native debuggers typically match breakpoints, while passing a partial path narrows the match. Given:

project/
  - src/utils.js
  - lib/utils.js
text

--probe utils.js:10 binds to both files and produces one hit per match. Each hit carries its own location field identifying where the expression was actually executed, so consumers can attribute the result to one of the two files accurately. To disambiguate at bind time, specify a fuller path that only matches the intended file:

$ node inspect --probe src/utils.js:10 --expr 'x' main.js   # matches only src/utils.js
console

Probe examples#

Probing a variable conditionally#
// app.js
let total = 0;
for (let i = 0; i < 10; i++) {
  total += i;  // line 4
}
js
$ out/Release/node inspect --probe app.js:4 --expr 'total' \
                           --cond 'i % 3 === 0' app.js
console
Hit 1 at file:///path/to/app.js:3:3
  total = 0
Hit 2 at file:///path/to/app.js:3:3
  total = 3
Hit 3 at file:///path/to/app.js:3:3
  total = 15
Hit 4 at file:///path/to/app.js:3:3
  total = 36
Completed
text

Advanced usage#

V8 inspector integration for Node.js#

V8 Inspector integration allows attaching Chrome DevTools to Node.js instances for debugging and profiling. It uses the Chrome DevTools Protocol.

V8 Inspector can be enabled by passing the --inspect flag when starting a Node.js application. It is also possible to supply a custom port with that flag, e.g. --inspect=9222 will accept DevTools connections on port 9222.

Using the --inspect flag will execute the code immediately before debugger is connected. This means that the code will start running before you can start debugging, which might not be ideal if you want to debug from the very beginning.

In such cases, you have two alternatives:

  1. --inspect-wait flag: This flag will wait for debugger to be attached before executing the code. This allows you to start debugging right from the beginning of the execution.
  2. --inspect-brk flag: Unlike --inspect, this flag will break on the first line of the code as soon as debugger is attached. This is useful when you want to debug the code step by step from the very beginning, without any code execution prior to debugging.

So, when deciding between --inspect, --inspect-wait, and --inspect-brk, consider whether you want the code to start executing immediately, wait for debugger to be attached before execution, or break on the first line for step-by-step debugging.

$ node --inspect index.js
Debugger listening on ws://127.0.0.1:9229/dc9010dd-f8b8-4ac5-a510-c1a114ec7d29
For help, see: https://nodejs.org/learn/getting-started/debugging
console

(In the example above, the UUID dc9010dd-f8b8-4ac5-a510-c1a114ec7d29 at the end of the URL is generated on the fly, it varies in different debugging sessions.)

Deprecated APIs#

Node.js APIs might be deprecated for any of the following reasons:

  • Use of the API is unsafe.
  • An improved alternative API is available.
  • Breaking changes to the API are expected in a future major release.

Node.js uses four kinds of deprecations:

  • Documentation-only
  • Application (non-node_modules code only)
  • Runtime (all code)
  • End-of-Life

A Documentation-only deprecation is one that is expressed only within the Node.js API docs. These generate no side-effects while running Node.js. Some Documentation-only deprecations trigger a runtime warning when launched with --pending-deprecation flag (or its alternative, NODE_PENDING_DEPRECATION=1 environment variable), similarly to Runtime deprecations below. Documentation-only deprecations that support that flag are explicitly labeled as such in the list of Deprecated APIs.

An Application deprecation for only non-node_modules code will, by default, generate a process warning that will be printed to stderr the first time the deprecated API is used in code that's not loaded from node_modules. When the --throw-deprecation command-line flag is used, a Runtime deprecation will cause an error to be thrown. When --pending-deprecation is used, warnings will also be emitted for code loaded from node_modules.

A runtime deprecation for all code is similar to the runtime deprecation for non-node_modules code, except that it also emits a warning for code loaded from node_modules.

An End-of-Life deprecation is used when functionality is or will soon be removed from Node.js.

Revoking deprecations#

Occasionally, the deprecation of an API might be reversed. In such situations, this document will be updated with information relevant to the decision. However, the deprecation identifier will not be modified.

List of deprecated APIs#

DEP0001: http.OutgoingMessage.prototype.flush#

Type: End-of-Life

OutgoingMessage.prototype.flush() has been removed. Use OutgoingMessage.prototype.flushHeaders() instead.

DEP0002: require('_linklist')#

Type: End-of-Life

The _linklist module is deprecated. Please use a userland alternative.

DEP0003: _writableState.buffer#

Type: End-of-Life

The _writableState.buffer has been removed. Use _writableState.getBuffer() instead.

DEP0004: CryptoStream.prototype.readyState#

Type: End-of-Life

The CryptoStream.prototype.readyState property was removed.

DEP0005: Buffer() constructor#

Type: Application (non-node_modules code only)

The Buffer() function and new Buffer() constructor are deprecated due to API usability issues that can lead to accidental security issues.

As an alternative, use one of the following methods of constructing Buffer objects:

Without --pending-deprecation, runtime warnings occur only for code not in node_modules. This means there will not be deprecation warnings for Buffer() usage in dependencies. With --pending-deprecation, a runtime warning results no matter where the Buffer() usage occurs.

DEP0006: child_process options.customFds#

Type: End-of-Life

Within the child_process module's spawn(), fork(), and exec() methods, the options.customFds option is deprecated. The options.stdio option should be used instead.

DEP0007: Replace cluster worker.suicide with worker.exitedAfterDisconnect#

Type: End-of-Life

In an earlier version of the Node.js cluster, a boolean property with the name suicide was added to the Worker object. The intent of this property was to provide an indication of how and why the Worker instance exited. In Node.js 6.0.0, the old property was deprecated and replaced with a new worker.exitedAfterDisconnect property. The old property name did not precisely describe the actual semantics and was unnecessarily emotion-laden.

DEP0008: require('node:constants')#

Type: Documentation-only

The node:constants module is deprecated. When requiring access to constants relevant to specific Node.js builtin modules, developers should instead refer to the constants property exposed by the relevant module. For instance, require('node:fs').constants and require('node:os').constants.

DEP0009: crypto.pbkdf2 without digest#

Type: End-of-Life

Use of the crypto.pbkdf2() API without specifying a digest was deprecated in Node.js 6.0 because the method defaulted to using the non-recommended 'SHA1' digest. Previously, a deprecation warning was printed. Starting in Node.js 8.0.0, calling crypto.pbkdf2() or crypto.pbkdf2Sync() with digest set to undefined will throw a TypeError.

Beginning in Node.js 11.0.0, calling these functions with digest set to null would print a deprecation warning to align with the behavior when digest is undefined.

Now, however, passing either undefined or null will throw a TypeError.

DEP0010: crypto.createCredentials#

Type: End-of-Life

The crypto.createCredentials() API was removed. Please use tls.createSecureContext() instead.

DEP0011: crypto.Credentials#

Type: End-of-Life

The crypto.Credentials class was removed. Please use tls.SecureContext instead.

DEP0012: Domain.dispose#

Type: End-of-Life

Domain.dispose() has been removed. Recover from failed I/O actions explicitly via error event handlers set on the domain instead.

DEP0013: fs asynchronous function without callback#

Type: End-of-Life

Calling an asynchronous function without a callback throws a TypeError in Node.js 10.0.0 onwards. See https://github.com/nodejs/node/pull/12562.

DEP0014: fs.read legacy String interface#

Type: End-of-Life

The fs.read() legacy String interface is deprecated. Use the Buffer API as mentioned in the documentation instead.

DEP0015: fs.readSync legacy String interface#

Type: End-of-Life

The fs.readSync() legacy String interface is deprecated. Use the Buffer API as mentioned in the documentation instead.

DEP0016: GLOBAL/root#

Type: End-of-Life

The GLOBAL and root aliases for the global property were deprecated in Node.js 6.0.0 and have since been removed.

DEP0017: Intl.v8BreakIterator#

Type: End-of-Life

Intl.v8BreakIterator was a non-standard extension and has been removed. See Intl.Segmenter.

DEP0018: Unhandled promise rejections#

Type: End-of-Life

Unhandled promise rejections are deprecated. By default, promise rejections that are not handled terminate the Node.js process with a non-zero exit code. To change the way Node.js treats unhandled rejections, use the --unhandled-rejections command-line option.

DEP0019: require('.') resolved outside directory#

Type: End-of-Life

In certain cases, require('.') could resolve outside the package directory. This behavior has been removed.

DEP0020: Server.connections#

Type: End-of-Life

The Server.connections property was deprecated in Node.js 0.9.7 and has been removed. Please use the Server.getConnections() method instead.

DEP0021: Server.listenFD#

Type: End-of-Life

The Server.listenFD() method was deprecated and removed. Please use Server.listen({fd: <number>}) instead.

DEP0022: os.tmpDir()#

Type: End-of-Life

The os.tmpDir() API was deprecated in Node.js 7.0.0 and has since been removed. Please use os.tmpdir() instead.

An automated migration is available (source):

npx codemod@latest @nodejs/tmpDir-to-tmpdir
bash

DEP0023: os.getNetworkInterfaces()#

Type: End-of-Life

The os.getNetworkInterfaces() method is deprecated. Please use the os.networkInterfaces() method instead.

DEP0024: REPLServer.prototype.convertToContext()#

Type: End-of-Life

The REPLServer.prototype.convertToContext() API has been removed.

DEP0025: require('node:sys')#

Type: Runtime

The node:sys module is deprecated. Please use the util module instead.

DEP0026: util.print()#

Type: End-of-Life

util.print() has been removed. Please use console.log() instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-print-to-console-log
bash

DEP0027: util.puts()#

Type: End-of-Life

util.puts() has been removed. Please use console.log() instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-print-to-console-log
bash

DEP0028: util.debug()#

Type: End-of-Life

util.debug() has been removed. Please use console.error() instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-print-to-console-log
bash

DEP0029: util.error()#

Type: End-of-Life

util.error() has been removed. Please use console.error() instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-print-to-console-log
bash

DEP0030: SlowBuffer#

Type: End-of-Life

The SlowBuffer class has been removed. Please use Buffer.allocUnsafeSlow(size) instead.

An automated migration is available (source).

npx codemod@latest @nodejs/slow-buffer-to-buffer-alloc-unsafe-slow
bash

DEP0031: ecdh.setPublicKey()#

Type: Runtime

The ecdh.setPublicKey() method is now deprecated as its inclusion in the API is not useful.

DEP0032: node:domain module#

Type: Documentation-only

The domain module is deprecated and should not be used.

DEP0033: EventEmitter.listenerCount()#

Type: Revoked

The events.listenerCount(emitter, eventName) API was deprecated, as it provided identical functionality to emitter.listenerCount(eventName). The deprecation was revoked because this function has been repurposed to also accept <EventTarget> arguments.

DEP0034: fs.exists(path, callback)#

Type: Documentation-only

The fs.exists(path, callback) API is deprecated. Please use fs.stat() or fs.access() instead.

DEP0035: fs.lchmod(path, mode, callback)#

Type: Documentation-only

The fs.lchmod(path, mode, callback) API is deprecated.

DEP0036: fs.lchmodSync(path, mode)#

Type: Documentation-only

The fs.lchmodSync(path, mode) API is deprecated.

DEP0037: fs.lchown(path, uid, gid, callback)#

Type: Deprecation revoked

The fs.lchown(path, uid, gid, callback) API was deprecated. The deprecation was revoked because the requisite supporting APIs were added in libuv.

DEP0038: fs.lchownSync(path, uid, gid)#

Type: Deprecation revoked

The fs.lchownSync(path, uid, gid) API was deprecated. The deprecation was revoked because the requisite supporting APIs were added in libuv.

DEP0039: require.extensions#

Type: Documentation-only

The require.extensions property is deprecated.

DEP0040: node:punycode module#

Type: Application (non-node_modules code only)

The punycode module is deprecated. Please use a userland alternative instead.

DEP0041: NODE_REPL_HISTORY_FILE environment variable#

Type: End-of-Life

The NODE_REPL_HISTORY_FILE environment variable was removed. Please use NODE_REPL_HISTORY instead.

DEP0042: tls.CryptoStream#

Type: End-of-Life

The tls.CryptoStream class was removed. Please use tls.TLSSocket instead.

DEP0043: tls.SecurePair#

Type: End-of-Life

The tls.SecurePair class is deprecated. Please use tls.TLSSocket instead.

DEP0044: util.isArray()#

Type: Runtime

The util.isArray() API is deprecated. Please use Array.isArray() instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0045: util.isBoolean()#

Type: End-of-Life

The util.isBoolean() API has been removed. Please use typeof arg === 'boolean' instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0046: util.isBuffer()#

Type: End-of-Life

The util.isBuffer() API has been removed. Please use Buffer.isBuffer() instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0047: util.isDate()#

Type: End-of-Life

The util.isDate() API has been removed. Please use arg instanceof Date instead.

Also for stronger approaches, consider using: Date.prototype.toString.call(arg) === '[object Date]' && !isNaN(arg). This can also be used in a try/catch block to handle invalid date objects.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0048: util.isError()#

Type: End-of-Life

The util.isError() API has been removed. Please use Error.isError(arg).

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0049: util.isFunction()#

Type: End-of-Life

The util.isFunction() API has been removed. Please use typeof arg === 'function' instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0050: util.isNull()#

Type: End-of-Life

The util.isNull() API has been removed. Please use arg === null instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0051: util.isNullOrUndefined()#

Type: End-of-Life

The util.isNullOrUndefined() API has been removed. Please use arg === null || arg === undefined instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0052: util.isNumber()#

Type: End-of-Life

The util.isNumber() API has been removed. Please use typeof arg === 'number' instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0053: util.isObject()#

Type: End-of-Life

The util.isObject() API has been removed. Please use arg && typeof arg === 'object' instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0054: util.isPrimitive()#

Type: End-of-Life

The util.isPrimitive() API has been removed. Please use Object(arg) !== arg instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0055: util.isRegExp()#

Type: End-of-Life

The util.isRegExp() API has been removed. Please use arg instanceof RegExp instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0056: util.isString()#

Type: End-of-Life

The util.isString() API has been removed. Please use typeof arg === 'string' instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0057: util.isSymbol()#

Type: End-of-Life

The util.isSymbol() API has been removed. Please use typeof arg === 'symbol' instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0058: util.isUndefined()#

Type: End-of-Life

The util.isUndefined() API has been removed. Please use arg === undefined instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-is
bash

DEP0059: util.log()#

Type: End-of-Life

The util.log() API has been removed because it's an unmaintained legacy API that was exposed to user land by accident. Instead, consider the following alternatives based on your specific needs:

  • Third-Party Logging Libraries

  • Use console.log(new Date().toLocaleString(), message)

By adopting one of these alternatives, you can transition away from util.log() and choose a logging strategy that aligns with the specific requirements and complexity of your application.

An automated migration is available (source):

npx codemod@latest @nodejs/util-log-to-console-log
bash

DEP0060: util._extend()#

Type: Runtime

The util._extend() API is deprecated because it's an unmaintained legacy API that was exposed to user land by accident. Please use target = Object.assign(target, source) instead.

An automated migration is available (source):

npx codemod@latest @nodejs/util-extend-to-object-assign
bash

DEP0061: fs.SyncWriteStream#

Type: End-of-Life

The fs.SyncWriteStream class was never intended to be a publicly accessible API and has been removed. No alternative API is available. Please use a userland alternative.

DEP0062: node --debug#

Type: End-of-Life

--debug activates the legacy V8 debugger interface, which was removed as of V8 5.8. It is replaced by Inspector which is activated with --inspect instead.

DEP0063: ServerResponse.prototype.writeHeader()#

Type: End-of-Life

The node:http module ServerResponse.prototype.writeHeader() API is deprecated. Please use ServerResponse.prototype.writeHead() instead.

The ServerResponse.prototype.writeHeader() method was never documented as an officially supported API.

DEP0064: tls.createSecurePair()#

Type: End-of-Life

The tls.createSecurePair() API was deprecated in documentation in Node.js 0.11.3. Users should use tls.Socket instead.

An automated migration is available (source):

npx codemod @nodejs/tls-create-secure-pair-to-tls-socket
bash

DEP0065: repl.REPL_MODE_MAGIC and NODE_REPL_MODE=magic#

Type: End-of-Life

The node:repl module's REPL_MODE_MAGIC constant, used for replMode option, has been removed. Its behavior has been functionally identical to that of REPL_MODE_SLOPPY since Node.js 6.0.0, when V8 5.0 was imported. Please use REPL_MODE_SLOPPY instead.

The NODE_REPL_MODE environment variable is used to set the underlying replMode of an interactive node session. Its value, magic, is also removed. Please use sloppy instead.

DEP0066: OutgoingMessage.prototype._headers, OutgoingMessage.prototype._headerNames#

Type: End-of-Life

The node:http module OutgoingMessage.prototype._headers and OutgoingMessage.prototype._headerNames properties are deprecated. Use one of the public methods (e.g. OutgoingMessage.prototype.getHeader(), OutgoingMessage.prototype.getHeaders(), OutgoingMessage.prototype.getHeaderNames(), OutgoingMessage.prototype.getRawHeaderNames(), OutgoingMessage.prototype.hasHeader(), OutgoingMessage.prototype.removeHeader(), OutgoingMessage.prototype.setHeader()) for working with outgoing headers.

The OutgoingMessage.prototype._headers and OutgoingMessage.prototype._headerNames properties were never documented as officially supported properties.

An automated migration is available (source):

npx codemod@latest @nodejs/http-outgoingmessage-headers
bash

DEP0067: OutgoingMessage.prototype._renderHeaders#

Type: Documentation-only

The node:http module OutgoingMessage.prototype._renderHeaders() API is deprecated.

The OutgoingMessage.prototype._renderHeaders property was never documented as an officially supported API.

DEP0068: node debug#

Type: End-of-Life

node debug corresponds to the legacy CLI debugger which has been replaced with a V8-inspector based CLI debugger available through node inspect.

DEP0069: vm.runInDebugContext(string)#

Type: End-of-Life

DebugContext has been removed in V8 and is not available in Node.js 10+.

DebugContext was an experimental API.

DEP0070: async_hooks.currentId()#

Type: End-of-Life

async_hooks.currentId() was renamed to async_hooks.executionAsyncId() for clarity.

This change was made while async_hooks was an experimental API.

DEP0071: async_hooks.triggerId()#

Type: End-of-Life

async_hooks.triggerId() was renamed to async_hooks.triggerAsyncId() for clarity.

This change was made while async_hooks was an experimental API.

DEP0072: async_hooks.AsyncResource.triggerId()#

Type: End-of-Life

async_hooks.AsyncResource.triggerId() was renamed to async_hooks.AsyncResource.triggerAsyncId() for clarity.

This change was made while async_hooks was an experimental API.

DEP0073: Several internal properties of net.Server#

Type: End-of-Life

Accessing several internal, undocumented properties of net.Server instances with inappropriate names is deprecated.

As the original API was undocumented and not generally useful for non-internal code, no replacement API is provided.

DEP0074: REPLServer.bufferedCommand#

Type: End-of-Life

The REPLServer.bufferedCommand property was deprecated in favor of REPLServer.clearBufferedCommand().

DEP0075: REPLServer.parseREPLKeyword()#

Type: End-of-Life

REPLServer.parseREPLKeyword() was removed from userland visibility.

DEP0076: tls.parseCertString()#

Type: End-of-Life

tls.parseCertString() was a trivial parsing helper that was made public by mistake. While it was supposed to parse certificate subject and issuer strings, it never handled multi-value Relative Distinguished Names correctly.

Earlier versions of this document suggested using querystring.parse() as an alternative to tls.parseCertString(). However, querystring.parse() also does not handle all certificate subjects correctly and should not be used.

DEP0077: Module._debug()#

Type: End-of-Life

Module._debug() has been removed.

The Module._debug() function was never documented as an officially supported API.

DEP0078: REPLServer.turnOffEditorMode()#

Type: End-of-Life

REPLServer.turnOffEditorMode() was removed from userland visibility.

DEP0079: Custom inspection function on objects via .inspect()#

Type: End-of-Life

Using a property named inspect on an object to specify a custom inspection function for util.inspect() is deprecated. Use util.inspect.custom instead. For backward compatibility with Node.js prior to version 6.4.0, both can be specified.

DEP0080: path._makeLong()#

Type: Documentation-only

The internal path._makeLong() was not intended for public use. However, userland modules have found it useful. The internal API is deprecated and replaced with an identical, public path.toNamespacedPath() method.

DEP0081: fs.truncate() using a file descriptor#

Type: End-of-Life

fs.truncate() fs.truncateSync() usage with a file descriptor is deprecated. Please use fs.ftruncate() or fs.ftruncateSync() to work with file descriptors.

An automated migration is available (source):

npx codemod@latest @nodejs/fs-truncate-fd-deprecation
bash

DEP0082: REPLServer.prototype.memory()#

Type: End-of-Life

REPLServer.prototype.memory() is only necessary for the internal mechanics of the REPLServer itself. Do not use this function.

DEP0083: Disabling ECDH by setting ecdhCurve to false#

Type: End-of-Life

The ecdhCurve option to tls.createSecureContext() and tls.TLSSocket could be set to false to disable ECDH entirely on the server only. This mode was deprecated in preparation for migrating to OpenSSL 1.1.0 and consistency with the client and is now unsupported. Use the ciphers parameter instead.

DEP0084: requiring bundled internal dependencies#

Type: End-of-Life

Since Node.js versions 4.4.0 and 5.2.0, several modules only intended for internal usage were mistakenly exposed to user code through require(). These modules were:

  • v8/tools/codemap
  • v8/tools/consarray
  • v8/tools/csvparser
  • v8/tools/logreader
  • v8/tools/profile_view
  • v8/tools/profile
  • v8/tools/SourceMap
  • v8/tools/splaytree
  • v8/tools/tickprocessor-driver
  • v8/tools/tickprocessor
  • node-inspect/lib/_inspect (from 7.6.0)
  • node-inspect/lib/internal/inspect_client (from 7.6.0)
  • node-inspect/lib/internal/inspect_repl (from 7.6.0)

The v8/* modules do not have any exports, and if not imported in a specific order would in fact throw errors. As such there are virtually no legitimate use cases for importing them through require().

On the other hand, node-inspect can be installed locally through a package manager, as it is published on the npm registry under the same name. No source code modification is necessary if that is done.

DEP0085: AsyncHooks sensitive API#

Type: End-of-Life

The AsyncHooks sensitive API was never documented and had various minor issues. Use the AsyncResource API instead. See https://github.com/nodejs/node/issues/15572.

DEP0086: Remove runInAsyncIdScope#

Type: End-of-Life

runInAsyncIdScope doesn't emit the 'before' or 'after' event and can thus cause a lot of issues. See https://github.com/nodejs/node/issues/14328.

DEP0089: require('node:assert')#

Type: Deprecation revoked

Importing assert directly was not recommended as the exposed functions use loose equality checks. The deprecation was revoked because use of the node:assert module is not discouraged, and the deprecation caused developer confusion.

DEP0090: Invalid GCM authentication tag lengths#

Type: End-of-Life

Node.js used to support all GCM authentication tag lengths which are accepted by OpenSSL when calling decipher.setAuthTag(). Beginning with Node.js v11.0.0, only authentication tag lengths of 128, 120, 112, 104, 96, 64, and 32 bits are allowed. Authentication tags of other lengths are invalid per NIST SP 800-38D.

DEP0091: crypto.DEFAULT_ENCODING#

Type: End-of-Life

The crypto.DEFAULT_ENCODING property only existed for compatibility with Node.js releases prior to versions 0.9.3 and has been removed.

DEP0092: Top-level this bound to module.exports#

Type: Documentation-only

Assigning properties to the top-level this as an alternative to module.exports is deprecated. Developers should use exports or module.exports instead.

DEP0093: crypto.fips is deprecated and replaced#

Type: Runtime

The crypto.fips property is deprecated. Please use crypto.setFips() and crypto.getFips() instead.

An automated migration is available (source).

npx codemod@latest @nodejs/crypto-fips-to-getFips
bash

DEP0094: Using assert.fail() with more than one argument#

Type: End-of-Life

Using assert.fail() with more than one argument is deprecated. Use assert.fail() with only one argument or use a different node:assert module method.

DEP0095: timers.enroll()#

Type: End-of-Life

timers.enroll() has been removed. Please use the publicly documented setTimeout() or setInterval() instead.

An automated migration is available (source).

npx codemod @nodejs/timers-deprecations
bash

DEP0096: timers.unenroll()#

Type: End-of-Life

timers.unenroll() has been removed. Please use the publicly documented clearTimeout() or clearInterval() instead.

An automated migration is available (source).

npx codemod @nodejs/timers-deprecations
bash

DEP0097: MakeCallback with domain property#

Type: Runtime

Users of MakeCallback that add the domain property to carry context, should start using the async_context variant of MakeCallback or CallbackScope, or the high-level AsyncResource class.

DEP0098: AsyncHooks embedder AsyncResource.emitBefore and AsyncResource.emitAfter APIs#

Type: End-of-Life

The embedded API provided by AsyncHooks exposes .emitBefore() and .emitAfter() methods which are very easy to use incorrectly which can lead to unrecoverable errors.

Use asyncResource.runInAsyncScope() API instead which provides a much safer, and more convenient, alternative. See https://github.com/nodejs/node/pull/18513.

DEP0099: Async context-unaware node::MakeCallback C++ APIs#

Type: Compile-time

Certain versions of node::MakeCallback APIs available to native addons are deprecated. Please use the versions of the API that accept an async_context parameter.

DEP0100: process.assert()#

Type: End-of-Life

process.assert() is deprecated. Please use the assert module instead.

This was never a documented feature.

An automated migration is available (source).

npx codemod@latest @nodejs/process-assert-to-node-assert
bash

DEP0101: --with-lttng#

Type: End-of-Life

The --with-lttng compile-time option has been removed.

DEP0102: Using noAssert in Buffer#(read|write) operations#

Type: End-of-Life

Using the noAssert argument has no functionality anymore. All input is verified regardless of the value of noAssert. Skipping the verification could lead to hard-to-find errors and crashes.

DEP0103: process.binding('util').is[...] typechecks#

Type: Documentation-only (supports --pending-deprecation)

Using process.binding() in general should be avoided. The type checking methods in particular can be replaced by using util.types.

This deprecation has been superseded by the deprecation of the process.binding() API (DEP0111).

DEP0104: process.env string coercion#

Type: Documentation-only (supports --pending-deprecation)

When assigning a non-string property to process.env, the assigned value is implicitly converted to a string. This behavior is deprecated if the assigned value is not a string, boolean, or number. In the future, such assignment might result in a thrown error. Please convert the property to a string before assigning it to process.env.

DEP0105: decipher.finaltol#

Type: End-of-Life

decipher.finaltol() has never been documented and was an alias for decipher.final(). This API has been removed, and it is recommended to use decipher.final() instead.

DEP0106: crypto.createCipher and crypto.createDecipher#

Type: End-of-Life

crypto.createCipher() and crypto.createDecipher() have been removed as they use a weak key derivation function (MD5 with no salt) and static initialization vectors. It is recommended to derive a key using crypto.pbkdf2() or crypto.scrypt() with random salts and to use crypto.createCipheriv() and crypto.createDecipheriv() to obtain the Cipheriv and Decipheriv objects respectively.

An automated migration is available (source):

npx codemod @nodejs/crypto-createcipheriv-migration
bash

DEP0107: tls.convertNPNProtocols()#

Type: End-of-Life

This was an undocumented helper function not intended for use outside Node.js core and obsoleted by the removal of NPN (Next Protocol Negotiation) support.

DEP0108: zlib.bytesRead#

Type: End-of-Life

Deprecated alias for zlib.bytesWritten. This original name was chosen because it also made sense to interpret the value as the number of bytes read by the engine, but is inconsistent with other streams in Node.js that expose values under these names.

An automated migration is available (source):

npx codemod@latest @nodejs/zlib-bytesread-to-byteswritten
bash

DEP0109: http, https, and tls support for invalid URLs#

Type: End-of-Life

Some previously supported (but strictly invalid) URLs were accepted through the http.request(), http.get(), https.request(), https.get(), and tls.checkServerIdentity() APIs because those were accepted by the legacy url.parse() API. The mentioned APIs now use the WHATWG URL parser that requires strictly valid URLs. Passing an invalid URL is deprecated and support will be removed in the future.

DEP0110: vm.Script cached data#

Type: Documentation-only

The produceCachedData option is deprecated. Use script.createCachedData() instead.

DEP0111: process.binding()#

Type: Documentation-only (supports --pending-deprecation)

process.binding() is for use by Node.js internal code only.

While process.binding() has not reached End-of-Life status in general, it is unavailable when the permission model is enabled.

DEP0112: dgram private APIs#

Type: End-of-Life

The node:dgram module previously contained several APIs that were never meant to accessed outside of Node.js core: Socket.prototype._handle, Socket.prototype._receiving, Socket.prototype._bindState, Socket.prototype._queue, Socket.prototype._reuseAddr, Socket.prototype._healthCheck(), Socket.prototype._stopReceiving(), and dgram._createSocketHandle(). These have been removed.

DEP0113: Cipher.setAuthTag(), Decipher.getAuthTag()#

Type: End-of-Life

Cipher.setAuthTag() and Decipher.getAuthTag() are no longer available. They were never documented and would throw when called.

DEP0114: crypto._toBuf()#

Type: End-of-Life

The crypto._toBuf() function was not designed to be used by modules outside of Node.js core and was removed.

DEP0115: crypto.prng(), crypto.pseudoRandomBytes(), crypto.rng()#

Type: Documentation-only (supports --pending-deprecation)

In recent versions of Node.js, there is no difference between crypto.randomBytes() and crypto.pseudoRandomBytes(). The latter is deprecated along with the undocumented aliases crypto.prng() and crypto.rng() in favor of crypto.randomBytes() and might be removed in a future release.

DEP0116: Legacy URL API#

Type: Deprecation revoked

The legacy URL API is deprecated. This includes url.format(), url.parse(), url.resolve(), and the legacy urlObject. Please use the WHATWG URL API instead.

An automated migration is available (source).

npx codemod@latest @nodejs/node-url-to-whatwg-url
bash

DEP0117: Native crypto handles#

Type: End-of-Life

Previous versions of Node.js exposed handles to internal native objects through the _handle property of the Cipher, Decipher, DiffieHellman, DiffieHellmanGroup, ECDH, Hash, Hmac, Sign, and Verify classes. The _handle property has been removed because improper use of the native object can lead to crashing the application.

DEP0118: dns.lookup() support for a falsy host name#

Type: End-of-Life

Previous versions of Node.js supported dns.lookup() with a falsy host name like dns.lookup(false) due to backward compatibility. This has been removed.

DEP0119: process.binding('uv').errname() private API#

Type: Documentation-only (supports --pending-deprecation)

process.binding('uv').errname() is deprecated. Please use util.getSystemErrorName() instead.

DEP0120: Windows Performance Counter support#

Type: End-of-Life

Windows Performance Counter support has been removed from Node.js. The undocumented COUNTER_NET_SERVER_CONNECTION(), COUNTER_NET_SERVER_CONNECTION_CLOSE(), COUNTER_HTTP_SERVER_REQUEST(), COUNTER_HTTP_SERVER_RESPONSE(), COUNTER_HTTP_CLIENT_REQUEST(), and COUNTER_HTTP_CLIENT_RESPONSE() functions have been deprecated.

DEP0121: net._setSimultaneousAccepts()#

Type: End-of-Life

The undocumented net._setSimultaneousAccepts() function was originally intended for debugging and performance tuning when using the node:child_process and node:cluster modules on Windows. The function is not generally useful and is being removed. See discussion here: https://github.com/nodejs/node/issues/18391

DEP0122: tls Server.prototype.setOptions()#

Type: End-of-Life

Please use Server.prototype.setSecureContext() instead.

DEP0123: setting the TLS ServerName to an IP address#

Type: End-of-Life

Setting the TLS ServerName to an IP address is not permitted by RFC 6066.

DEP0124: using REPLServer.rli#

Type: End-of-Life

This property is a reference to the instance itself.

DEP0125: require('node:_stream_wrap')#

Type: End-of-Life

The node:_stream_wrap module is deprecated.

DEP0126: timers.active()#

Type: End-of-Life

The previously undocumented timers.active() has been removed. Please use the publicly documented timeout.refresh() instead. If re-referencing the timeout is necessary, timeout.ref() can be used with no performance impact since Node.js 10.

An automated migration is available (source).

npx codemod @nodejs/timers-deprecations
bash

DEP0127: timers._unrefActive()#

Type: End-of-Life

The previously undocumented and "private" timers._unrefActive() has been removed. Please use the publicly documented timeout.refresh() instead. If unreferencing the timeout is necessary, timeout.unref() can be used with no performance impact since Node.js 10.

An automated migration is available (source).

npx codemod @nodejs/timers-deprecations
bash

DEP0128: modules with an invalid main entry and an index.js file#

Type: Runtime

Modules that have an invalid main entry (e.g., ./does-not-exist.js) and also have an index.js file in the top level directory will resolve the index.js file. That is deprecated and is going to throw an error in future Node.js versions.

DEP0129: ChildProcess._channel#

Type: End-of-Life

The _channel property of child process objects returned by spawn() and similar functions is not intended for public use. Use ChildProcess.channel instead.

DEP0130: Module.createRequireFromPath()#

Type: End-of-Life

Use module.createRequire() instead.

An automated migration is available (source):

npx codemod@latest @nodejs/create-require-from-path
bash

DEP0131: Legacy HTTP parser#

Type: End-of-Life

The legacy HTTP parser, used by default in versions of Node.js prior to 12.0.0, is deprecated and has been removed in v13.0.0. Prior to v13.0.0, the --http-parser=legacy command-line flag could be used to revert to using the legacy parser.

DEP0132: worker.terminate() with callback#

Type: End-of-Life

Passing a callback to worker.terminate() is deprecated. Use the returned Promise instead, or a listener to the worker's 'exit' event.

DEP0133: http connection#

Type: Documentation-only

Prefer response.socket over response.connection and request.socket over request.connection.

DEP0134: process._tickCallback#

Type: Documentation-only (supports --pending-deprecation)

The process._tickCallback property was never documented as an officially supported API.

DEP0135: WriteStream.open() and ReadStream.open() are internal#

Type: End-of-Life

WriteStream.open() and ReadStream.open() are undocumented internal APIs that do not make sense to use in userland. File streams should always be opened through their corresponding factory methods fs.createWriteStream() and fs.createReadStream()) or by passing a file descriptor in options.

DEP0136: http finished#

Type: Documentation-only

response.finished indicates whether response.end() has been called, not whether 'finish' has been emitted and the underlying data is flushed.

Use response.writableFinished or response.writableEnded accordingly instead to avoid the ambiguity.

To maintain existing behavior response.finished should be replaced with response.writableEnded.

DEP0137: Closing fs.FileHandle on garbage collection#

Type: End-of-Life

Allowing a fs.FileHandle object to be closed on garbage collection used to be allowed, but now throws an error.

Please ensure that all fs.FileHandle objects are explicitly closed using FileHandle.prototype.close() when the fs.FileHandle is no longer needed:

const fsPromises = require('node:fs').promises;
async function openAndClose() {
  let filehandle;
  try {
    filehandle = await fsPromises.open('thefile.txt', 'r');
  } finally {
    if (filehandle !== undefined)
      await filehandle.close();
  }
}
js

DEP0138: process.mainModule#

Type: Documentation-only

process.mainModule is a CommonJS-only feature while process global object is shared with non-CommonJS environment. Its use within ECMAScript modules is unsupported.

It is deprecated in favor of require.main, because it serves the same purpose and is only available on CommonJS environment.

An automated migration is available (source):

npx codemod@latest @nodejs/process-main-module
bash

DEP0139: process.umask() with no arguments#

Type: Documentation-only

Calling process.umask() with no argument causes the process-wide umask to be written twice. This introduces a race condition between threads, and is a potential security vulnerability. There is no safe, cross-platform alternative API.

DEP0140: Use request.destroy() instead of request.abort()#

Type: Documentation-only

Use request.destroy() instead of request.abort().

DEP0141: repl.inputStream and repl.outputStream#

Type: Documentation-only (supports --pending-deprecation)

The node:repl module exported the input and output stream twice. Use .input instead of .inputStream and .output instead of .outputStream.

DEP0142: repl._builtinLibs#

Type: Documentation-only (supports --pending-deprecation)

The node:repl module exports a _builtinLibs property that contains an array of built-in modules. It was incomplete so far and instead it's better to rely upon require('node:module').builtinModules.

An automated migration is available (source):

npx codemod@latest @nodejs/repl-builtin-modules
bash

DEP0143: Transform._transformState#

Type: End-of-Life

Transform._transformState will be removed in future versions where it is no longer required due to simplification of the implementation.

DEP0144: module.parent#

Type: Documentation-only (supports --pending-deprecation)

A CommonJS module can access the first module that required it using module.parent. This feature is deprecated because it does not work consistently in the presence of ECMAScript modules and because it gives an inaccurate representation of the CommonJS module graph.

Some modules use it to check if they are the entry point of the current process. Instead, it is recommended to compare require.main and module:

if (require.main === module) {
  // Code section that will run only if current file is the entry point.
}
js

When looking for the CommonJS modules that have required the current one, require.cache and module.children can be used:

const moduleParents = Object.values(require.cache)
  .filter((m) => m.children.includes(module));
js

DEP0145: socket.bufferSize#

Type: Documentation-only

socket.bufferSize is just an alias for writable.writableLength.

DEP0146: new crypto.Certificate()#

Type: Documentation-only

The crypto.Certificate() constructor is deprecated. Use static methods of crypto.Certificate() instead.

DEP0147: fs.rmdir(path, { recursive: true })#

Type: End-of-Life

The fs.rmdir, fs.rmdirSync, and fs.promises.rmdir methods used to support a recursive option. That option has been removed.

Use fs.rm(path, { recursive: true, force: true }), fs.rmSync(path, { recursive: true, force: true }) or fs.promises.rm(path, { recursive: true, force: true }) instead.

An automated migration is available (source):

npx codemod@latest @nodejs/rmdir
bash

DEP0148: Folder mappings in "exports" (trailing "/")#

Type: End-of-Life

Using a trailing "/" to define subpath folder mappings in the subpath exports or subpath imports fields is no longer supported. Use subpath patterns instead.

DEP0149: http.IncomingMessage#connection#

Type: Documentation-only

Prefer message.socket over message.connection.

DEP0150: Changing the value of process.config#

Type: End-of-Life

The process.config property provides access to Node.js compile-time settings. However, the property is mutable and therefore subject to tampering. The ability to change the value will be removed in a future version of Node.js.

DEP0151: Main index lookup and extension searching#

Type: Runtime

Previously, index.js and extension searching lookups would apply to import 'pkg' main entry point resolution, even when resolving ES modules.

With this deprecation, all ES module main entry point resolutions require an explicit "exports" or "main" entry with the exact file extension.

DEP0152: Extension PerformanceEntry properties#

Type: End-of-Life

The 'gc', 'http2', and 'http' <PerformanceEntry> object types used to have additional properties assigned to them that provide additional information. These properties are now available within the standard detail property of the PerformanceEntry object. The deprecated accessors have been removed.

DEP0153: dns.lookup and dnsPromises.lookup options type coercion#

Type: End-of-Life

Using a non-nullish non-integer value for family option, a non-nullish non-number value for hints option, a non-nullish non-boolean value for all option, or a non-nullish non-boolean value for verbatim option in dns.lookup() and dnsPromises.lookup() throws an ERR_INVALID_ARG_TYPE error.

DEP0154: RSA-PSS generate key pair options#

Type: End-of-Life

Use 'hashAlgorithm' instead of 'hash', and 'mgf1HashAlgorithm' instead of 'mgf1Hash'.

An automated migration is available (source):

npx codemod@latest @nodejs/crypto-rsa-pss-update
bash

DEP0155: Trailing slashes in pattern specifier resolutions#

Type: Runtime

The remapping of specifiers ending in "/" like import 'pkg/x/' is deprecated for package "exports" and "imports" pattern resolutions.

DEP0156: .aborted property and 'abort', 'aborted' event in http#

Type: Documentation-only

Move to <Stream> API instead, as the http.ClientRequest, http.ServerResponse, and http.IncomingMessage are all stream-based. Check stream.destroyed instead of the .aborted property, and listen for 'close' instead of 'abort', 'aborted' event.

The .aborted property and 'abort' event are only useful for detecting .abort() calls. For closing a request early, use the Stream .destroy([error]) then check the .destroyed property and 'close' event should have the same effect. The receiving end should also check the readable.readableEnded value on http.IncomingMessage to get whether it was an aborted or graceful destroy.

DEP0157: Thenable support in streams#

Type: End-of-Life

An undocumented feature of Node.js streams was to support thenables in implementation methods. This is now deprecated, use callbacks instead and avoid use of async function for streams implementation methods.

This feature caused users to encounter unexpected problems where the user implements the function in callback style but uses e.g. an async method which would cause an error since mixing promise and callback semantics is not valid.

const w = new Writable({
  async final(callback) {
    await someOp();
    callback();
  },
});
js

DEP0158: buffer.slice(start, end)#

Type: Documentation-only

This method was deprecated because it is not compatible with Uint8Array.prototype.slice(), which is a superclass of Buffer.

Use buffer.subarray which does the same thing instead.

DEP0159: ERR_INVALID_CALLBACK#

Type: End-of-Life

This error code was removed due to adding more confusion to the errors used for value type validation.

An automated migration is available (source):

npx codemod @nodejs/err-invalid-callback
bash

DEP0160: process.on('multipleResolves', handler)#

Type: End-of-Life

This event was deprecated and removed because it did not work with V8 promise combinators which diminished its usefulness.

DEP0161: process._getActiveRequests() and process._getActiveHandles()#

Type: Documentation-only

The process._getActiveHandles() and process._getActiveRequests() functions are not intended for public use and can be removed in future releases.

Use process.getActiveResourcesInfo() to get a list of types of active resources and not the actual references.

DEP0162: fs.write(), fs.writeFileSync() coercion to string#

Type: End-of-Life

Implicit coercion of objects with own toString property, passed as second parameter in fs.write(), fs.writeFile(), fs.appendFile(), fs.writeFileSync(), and fs.appendFileSync() is deprecated. Convert them to primitive strings.

DEP0163: channel.subscribe(onMessage), channel.unsubscribe(onMessage)#

Type: Deprecation revoked

These methods were deprecated because their use could leave the channel object vulnerable to being garbage-collected if not strongly referenced by the user. The deprecation was revoked because channel objects are now resistant to garbage collection when the channel has active subscribers.

DEP0164: process.exit(code), process.exitCode coercion to integer#

Type: End-of-Life

Values other than undefined, null, integer numbers, and integer strings (e.g., '1') are deprecated as value for the code parameter in process.exit() and as value to assign to process.exitCode.

DEP0165: --trace-atomics-wait#

Type: End-of-Life

The --trace-atomics-wait flag has been removed because it uses the V8 hook SetAtomicsWaitCallback, that will be removed in a future V8 release.

DEP0166: Double slashes in imports and exports targets#

Type: Runtime

Package imports and exports targets mapping into paths including a double slash (of "/" or "\") are deprecated and will fail with a resolution validation error in a future release. This same deprecation also applies to pattern matches starting or ending in a slash.

DEP0167: Weak DiffieHellmanGroup instances (modp1, modp2, modp5)#

Type: Documentation-only

The well-known MODP groups modp1, modp2, and modp5 are deprecated because they are not secure against practical attacks. See RFC 8247 Section 2.4 for details.

These groups might be removed in future versions of Node.js. Applications that rely on these groups should evaluate using stronger MODP groups instead.

DEP0168: Unhandled exception in Node-API callbacks#

Type: Runtime

The implicit suppression of uncaught exceptions in Node-API callbacks is now deprecated.

Set the flag --force-node-api-uncaught-exceptions-policy to force Node.js to emit an 'uncaughtException' event if the exception is not handled in Node-API callbacks.

DEP0169: Insecure url.parse()#

Type: Application (non-node_modules code only)

url.parse() behavior is not standardized and prone to errors that have security implications. Use the WHATWG URL API instead. CVEs are not issued for url.parse() vulnerabilities.

Calling url.format(urlString) or url.resolve() invokes url.parse() internally, and is therefore also covered by this deprecation.

DEP0170: Invalid port when using url.parse()#

Type: End-of-Life

url.parse() used to accept URLs with ports that are not numbers. This behavior might result in host name spoofing with unexpected input. These URLs will throw an error (which the WHATWG URL API also does).

DEP0171: Setters for http.IncomingMessage headers and trailers#

Type: Documentation-only

In a future version of Node.js, message.headers, message.headersDistinct, message.trailers, and message.trailersDistinct will be read-only.

DEP0172: The asyncResource property of AsyncResource bound functions#

Type: End-of-Life

Older versions of Node.js would add the asyncResource when a function is bound to an AsyncResource. It no longer does.

DEP0173: the assert.CallTracker class#

Type: End-of-Life

The assert.CallTracker API has been removed.

DEP0174: calling promisify on a function that returns a Promise#

Type: Runtime

Calling util.promisify on a function that returns a Promise will ignore the result of said promise, which can lead to unhandled promise rejections.

DEP0175: util.toUSVString#

Type: Documentation-only

The util.toUSVString() API is deprecated. Please use String.prototype.toWellFormed instead.

DEP0176: fs.F_OK, fs.R_OK, fs.W_OK, fs.X_OK#

Type: End-of-Life

F_OK, R_OK, W_OK and X_OK getters exposed directly on node:fs were removed. Get them from fs.constants or fs.promises.constants instead.

An automated migration is available (source):

npx codemod@latest @nodejs/fs-access-mode-constants
bash

DEP0177: util.types.isWebAssemblyCompiledModule#

Type: End-of-Life

The util.types.isWebAssemblyCompiledModule API has been removed. Please use value instanceof WebAssembly.Module instead.

DEP0178: dirent.path#

Type: End-of-Life

The dirent.path property has been removed due to its lack of consistency across release lines. Please use dirent.parentPath instead.

An automated migration is available (source):

npx codemod@latest @nodejs/dirent-path-to-parent-path
bash

DEP0179: Hash constructor#

Type: Runtime

Calling Hash class directly with Hash() or new Hash() is deprecated due to being internals, not intended for public use. Please use the crypto.createHash() method to create Hash instances.

DEP0180: fs.Stats constructor#

Type: Runtime

Calling fs.Stats class directly with Stats() or new Stats() is deprecated due to being internals, not intended for public use.

DEP0181: Hmac constructor#

Type: Runtime

Calling Hmac class directly with Hmac() or new Hmac() is deprecated due to being internals, not intended for public use. Please use the crypto.createHmac() method to create Hmac instances.

DEP0182: Short GCM authentication tags without explicit authTagLength#

Type: End-of-Life

For ciphers in GCM mode, the decipher.setAuthTag() function used to accept authentication tags of any valid length (see also DEP0090). This exception has been removed to better align with recommendations per NIST SP 800-38D, and applications that intend to use authentication tags that are shorter than the default authentication tag length (i.e., shorter than 16 bytes for AES-GCM) must explicitly set the authTagLength option of the crypto.createDecipheriv() function to the appropriate length.

DEP0183: OpenSSL engine-based APIs#

Type: Documentation-only

OpenSSL 3 has deprecated support for custom engines with a recommendation to switch to its new provider model. The clientCertEngine option for https.request(), tls.createSecureContext(), and tls.createServer(); the privateKeyEngine and privateKeyIdentifier for tls.createSecureContext(); and crypto.setEngine() all depend on this functionality from OpenSSL.

DEP0184: Instantiating node:zlib classes without new#

Type: Runtime

Instantiating classes without the new qualifier exported by the node:zlib module is deprecated. It is recommended to use the new qualifier instead. This applies to all Zlib classes, such as Deflate, DeflateRaw, Gunzip, Inflate, InflateRaw, Unzip, and Zlib.

DEP0185: Instantiating node:repl classes without new#

Type: End-of-Life

Instantiating classes without the new qualifier exported by the node:repl module is deprecated. The new qualifier must be used instead. This applies to all REPL classes, including REPLServer and Recoverable.

An automated migration is available (source):

npx codemod@latest @nodejs/repl-classes-with-new
bash

DEP0187: Passing invalid argument types to fs.existsSync#

Type: Runtime

Passing non-supported argument types is deprecated and, instead of returning false, will throw an error in a future version.

DEP0188: process.features.ipv6 and process.features.uv#

Type: Documentation-only

These properties are unconditionally true. Any checks based on these properties are redundant.

DEP0189: process.features.tls_*#

Type: Documentation-only

process.features.tls_alpn, process.features.tls_ocsp, and process.features.tls_sni are deprecated, as their values are guaranteed to be identical to that of process.features.tls.

DEP0190: Passing args to node:child_process execFile/spawn with shell option#

Type: Runtime

When an args array is passed to child_process.execFile or child_process.spawn with the option { shell: true } or { shell: '/path/to/shell' }, the values are not escaped, only space-separated, which can lead to shell injection.

DEP0191: repl.builtinModules#

Type: Documentation-only (supports --pending-deprecation)

The node:repl module exports a builtinModules property that contains an array of built-in modules. This was incomplete and matched the already deprecated repl._builtinLibs (DEP0142) instead it's better to rely upon require('node:module').builtinModules.

An automated migration is available (source):

npx codemod@latest @nodejs/repl-builtin-modules
bash

DEP0192: require('node:_tls_common') and require('node:_tls_wrap')#

Type: Runtime

The node:_tls_common and node:_tls_wrap modules are deprecated as they should be considered an internal nodejs implementation rather than a public facing API, use node:tls instead.

DEP0193: require('node:_stream_*')#

Type: End-of-Life

The node:_stream_duplex, node:_stream_passthrough, node:_stream_readable, node:_stream_transform, node:_stream_wrap and node:_stream_writable modules are deprecated as they should be considered an internal nodejs implementation rather than a public facing API, use node:stream instead.

DEP0194: HTTP/2 priority signaling#

Type: End-of-Life

The support for priority signaling has been removed following its deprecation in the RFC 9113.

An automated migration is available (source):

npx codemod@latest @nodejs/http2-priority-signaling
bash

DEP0195: Instantiating node:http classes without new#

Type: Documentation-only

Instantiating classes without the new qualifier exported by the node:http module is deprecated. It is recommended to use the new qualifier instead. This applies to all http classes, such as OutgoingMessage, IncomingMessage, ServerResponse and ClientRequest.

An automated migration is available (source):

npx codemod@latest @nodejs/http-classes-with-new
bash

DEP0196: Calling node:child_process functions with options.shell as an empty string#

Type: Documentation-only

Calling the process-spawning functions with { shell: '' } is almost certainly unintentional, and can cause aberrant behavior.

To make child_process.execFile or child_process.spawn invoke the default shell, use { shell: true }. If the intention is not to invoke a shell (default behavior), either omit the shell option, or set it to false or a nullish value.

To make child_process.exec invoke the default shell, either omit the shell option, or set it to a nullish value. If the intention is not to invoke a shell, use child_process.execFile instead.

DEP0197: util.types.isNativeError()#

Type: Documentation-only

The util.types.isNativeError API is deprecated. Please use Error.isError instead.

An automated migration is available (source):

npx codemod@latest @nodejs/types-is-native-error
bash

DEP0198: Creating SHAKE-128 and SHAKE-256 digests without an explicit options.outputLength#

Type: Runtime

Creating SHAKE-128 and SHAKE-256 digests without an explicit options.outputLength is deprecated.

DEP0199: require('node:_http_*')#

Type: Documentation-only

The node:_http_agent, node:_http_client, node:_http_common, node:_http_incoming, node:_http_outgoing and node:_http_server modules are deprecated as they should be considered an internal nodejs implementation rather than a public facing API, use node:http instead.

DEP0200: Closing fs.Dir on garbage collection#

Type: Documentation-only

Allowing a fs.Dir object to be closed on garbage collection is deprecated. In the future, doing so might result in a thrown error that will terminate the process.

Please ensure that all fs.Dir objects are explicitly closed using Dir.prototype.close() or using keyword:

import { opendir } from 'node:fs/promises';

{
  await using dir = await opendir('/async/disposable/directory');
} // Closed by dir[Symbol.asyncDispose]()

{
  using dir = await opendir('/sync/disposable/directory');
} // Closed by dir[Symbol.dispose]()

{
  const dir = await opendir('/unconditionally/iterated/directory');
  for await (const entry of dir) {
    // process an entry
  } // Closed by iterator
}

{
  let dir;
  try {
    dir = await opendir('/legacy/closeable/directory');
  } finally {
    await dir?.close();
  }
}
mjs

DEP0201: Passing options.type to Duplex.toWeb()#

Type: Runtime

Passing the type option to Duplex.toWeb() is deprecated. To specify the type of the readable half of the constructed readable-writable pair, use the readableType option instead.

DEP0202: Http1IncomingMessage and Http1ServerResponse options of HTTP/2 servers#

Type: Documentation-only

The Http1IncomingMessage and Http1ServerResponse options of http2.createServer() and http2.createSecureServer() are deprecated. Use http1Options.IncomingMessage and http1Options.ServerResponse instead.

// Deprecated
const server = http2.createSecureServer({
  allowHTTP1: true,
  Http1IncomingMessage: MyIncomingMessage,
  Http1ServerResponse: MyServerResponse,
});
cjs
// Use this instead
const server = http2.createSecureServer({
  allowHTTP1: true,
  http1Options: {
    IncomingMessage: MyIncomingMessage,
    ServerResponse: MyServerResponse,
  },
});
cjs

DEP0203: Passing CryptoKey to node:crypto APIs#

Type: Runtime

Passing a CryptoKey to node:crypto functions is deprecated and will throw an error in a future version. This includes crypto.createPublicKey(), crypto.createPrivateKey(), crypto.sign(), crypto.verify(), crypto.publicEncrypt(), crypto.publicDecrypt(), crypto.privateEncrypt(), crypto.privateDecrypt(), Sign.prototype.sign(), Verify.prototype.verify(), crypto.createHmac(), crypto.createCipheriv(), crypto.createDecipheriv(), crypto.encapsulate(), and crypto.decapsulate().

DEP0204: KeyObject.from() with non-extractable CryptoKey#

Type: Runtime

Passing a non-extractable CryptoKey to KeyObject.from() is deprecated and will throw an error in a future version.

DEP0205: module.register()#

Type: Runtime

module.register() is deprecated. Use module.registerHooks() instead.

The module.register() API provides off-thread async hooks for customizing ES modules; the module.registerHooks() API provides similar hooks that are synchronous, in-thread, and work for all types of modules. Supporting async hooks has proven to be complex, involving worker threads orchestration, and there are issues that have proven unresolvable. See caveats of asynchronous customization hooks. Please migrate to module.registerHooks() as soon as possible as module.register() will be removed in a future version of Node.js.

DEP0206: Calling digest() on an already-finalized Hmac instance#

Type: Documentation-only

Calling hmac.digest() more than once returns an empty buffer instead of throwing an error. This behavior is inconsistent with hash.digest() and may lead to subtle bugs. Calling hmac.digest() on a finalized Hmac instance will throw an error in a future version.

DEP0208: Server.prototype._listen2#

Type: Documentation-only

net.Server.prototype._listen2 is an undocumented alias for an internal function that sets up the listening handle. It is kept only so that code replacing it keeps being called by server.listen(), and it will be removed in a future version of Node.js. Use server.listen() instead of calling or overriding _listen2.

DEP0209: Using AbortSignal to dispose of resources#

Type: Documentation-only

Using AbortSignal to destroy long-lived resources is deprecated. Prefer using for resource cleanup.

AbortSignal is still a good fit for canceling actions, propagating cancellation from the outside, and timeouts.

// Deprecated
async function example() {
  const ac = new AbortController();
  const server = http.createServer(handler);
  server.listen({ port: 3000, signal: ac.signal });

  await doWork();
  ac.abort();
}
js
// Use this instead
async function example() {
  await using server = http.createServer(handler);
  server.listen(3000);

  await doWork();
}
js
// Deprecated
async function example() {
  const ac = new AbortController();
  const stream = addAbortSignal(ac.signal, fs.createReadStream(file));

  await consume(stream);
  ac.abort();
}
js
// Use this instead
async function example() {
  await using stream = fs.createReadStream(file);

  await consume(stream);
}
js
// Deprecated
async function example() {
  const ac = new AbortController();
  const child = spawn(command, args, { signal: ac.signal });

  await doWork();
  ac.abort();
}
js
// Use this instead
async function example() {
  using child = spawn(command, args);

  await doWork();
}
js

DEP0210: sqlite.DatabaseSync#

Type: Documentation-only

node:sqlite's DatabaseSync class was renamed to Database. DatabaseSync is kept as a deprecated alias. Use Database instead.

DEP0211: sqlite.StatementSync#

Type: Documentation-only

node:sqlite's StatementSync class was renamed to Statement. StatementSync is kept as a deprecated alias. Use Statement instead.

Diagnostic report#

Stability: 2 - Stable

Delivers a JSON-formatted diagnostic summary, written to a file.

The report is intended for development, test, and production use, to capture and preserve information for problem determination. It includes JavaScript and native stack traces, heap statistics, platform information, resource usage etc. With the report option enabled, diagnostic reports can be triggered on unhandled exceptions, fatal errors and user signals, in addition to triggering programmatically through API calls.

A complete example report that was generated on an uncaught exception is provided below for reference.

{
  "header": {
    "reportVersion": 5,
    "event": "exception",
    "trigger": "Exception",
    "filename": "report.20181221.005011.8974.0.001.json",
    "dumpEventTime": "2018-12-21T00:50:11Z",
    "dumpEventTimeStamp": "1545371411331",
    "processId": 8974,
    "cwd": "/home/nodeuser/project/node",
    "commandLine": [
      "/home/nodeuser/project/node/out/Release/node",
      "--report-uncaught-exception",
      "/home/nodeuser/project/node/test/report/test-exception.js",
      "child"
    ],
    "nodejsVersion": "v12.0.0-pre",
    "glibcVersionRuntime": "2.17",
    "glibcVersionCompiler": "2.17",
    "wordSize": "64 bit",
    "arch": "x64",
    "platform": "linux",
    "componentVersions": {
      "node": "12.0.0-pre",
      "v8": "7.1.302.28-node.5",
      "uv": "1.24.1",
      "zlib": "1.2.11",
      "ares": "1.15.0",
      "modules": "68",
      "nghttp2": "1.34.0",
      "napi": "3",
      "llhttp": "1.0.1",
      "openssl": "1.1.0j"
    },
    "release": {
      "name": "node"
    },
    "osName": "Linux",
    "osRelease": "3.10.0-862.el7.x86_64",
    "osVersion": "#1 SMP Wed Mar 21 18:14:51 EDT 2018",
    "osMachine": "x86_64",
    "cpus": [
      {
        "model": "Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz",
        "speed": 2700,
        "user": 88902660,
        "nice": 0,
        "sys": 50902570,
        "idle": 241732220,
        "irq": 0
      },
      {
        "model": "Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz",
        "speed": 2700,
        "user": 88902660,
        "nice": 0,
        "sys": 50902570,
        "idle": 241732220,
        "irq": 0
      }
    ],
    "networkInterfaces": [
      {
        "name": "en0",
        "internal": false,
        "mac": "13:10:de:ad:be:ef",
        "address": "10.0.0.37",
        "netmask": "255.255.255.0",
        "family": "IPv4"
      }
    ],
    "host": "test_machine"
  },
  "javascriptStack": {
    "message": "Error: *** test-exception.js: throwing uncaught Error",
    "stack": [
      "at myException (/home/nodeuser/project/node/test/report/test-exception.js:9:11)",
      "at Object.<anonymous> (/home/nodeuser/project/node/test/report/test-exception.js:12:3)",
      "at Module._compile (internal/modules/cjs/loader.js:718:30)",
      "at Object.Module._extensions..js (internal/modules/cjs/loader.js:729:10)",
      "at Module.load (internal/modules/cjs/loader.js:617:32)",
      "at tryModuleLoad (internal/modules/cjs/loader.js:560:12)",
      "at Function.Module._load (internal/modules/cjs/loader.js:552:3)",
      "at Function.Module.runMain (internal/modules/cjs/loader.js:771:12)",
      "at executeUserCode (internal/bootstrap/node.js:332:15)"
    ]
  },
  "nativeStack": [
    {
      "pc": "0x000055b57f07a9ef",
      "symbol": "report::GetNodeReport(v8::Isolate*, node::Environment*, char const*, char const*, v8::Local<v8::String>, std::ostream&) [./node]"
    },
    {
      "pc": "0x000055b57f07cf03",
      "symbol": "report::GetReport(v8::FunctionCallbackInfo<v8::Value> const&) [./node]"
    },
    {
      "pc": "0x000055b57f1bccfd",
      "symbol": " [./node]"
    },
    {
      "pc": "0x000055b57f1be048",
      "symbol": "v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*) [./node]"
    },
    {
      "pc": "0x000055b57feeda0e",
      "symbol": " [./node]"
    }
  ],
  "javascriptHeap": {
    "totalMemory": 5660672,
    "executableMemory": 524288,
    "totalCommittedMemory": 5488640,
    "availableMemory": 4341379928,
    "totalGlobalHandlesMemory": 8192,
    "usedGlobalHandlesMemory": 3136,
    "usedMemory": 4816432,
    "memoryLimit": 4345298944,
    "mallocedMemory": 254128,
    "externalMemory": 315644,
    "peakMallocedMemory": 98752,
    "nativeContextCount": 1,
    "detachedContextCount": 0,
    "doesZapGarbage": 0,
    "heapSpaces": {
      "read_only_space": {
        "memorySize": 524288,
        "committedMemory": 39208,
        "capacity": 515584,
        "used": 30504,
        "available": 485080
      },
      "new_space": {
        "memorySize": 2097152,
        "committedMemory": 2019312,
        "capacity": 1031168,
        "used": 985496,
        "available": 45672
      },
      "old_space": {
        "memorySize": 2273280,
        "committedMemory": 1769008,
        "capacity": 1974640,
        "used": 1725488,
        "available": 249152
      },
      "code_space": {
        "memorySize": 696320,
        "committedMemory": 184896,
        "capacity": 152128,
        "used": 152128,
        "available": 0
      },
      "map_space": {
        "memorySize": 536576,
        "committedMemory": 344928,
        "capacity": 327520,
        "used": 327520,
        "available": 0
      },
      "large_object_space": {
        "memorySize": 0,
        "committedMemory": 0,
        "capacity": 1520590336,
        "used": 0,
        "available": 1520590336
      },
      "new_large_object_space": {
        "memorySize": 0,
        "committedMemory": 0,
        "capacity": 0,
        "used": 0,
        "available": 0
      }
    }
  },
  "resourceUsage": {
    "rss": "35766272",
    "free_memory": "1598337024",
    "total_memory": "17179869184",
    "available_memory": "1598337024",
    "maxRss": "36624662528",
    "constrained_memory": "36624662528",
    "userCpuSeconds": 0.040072,
    "kernelCpuSeconds": 0.016029,
    "cpuConsumptionPercent": 5.6101,
    "userCpuConsumptionPercent": 4.0072,
    "kernelCpuConsumptionPercent": 1.6029,
    "pageFaults": {
      "IORequired": 0,
      "IONotRequired": 4610
    },
    "fsActivity": {
      "reads": 0,
      "writes": 0
    }
  },
  "uvthreadResourceUsage": {
    "userCpuSeconds": 0.039843,
    "kernelCpuSeconds": 0.015937,
    "cpuConsumptionPercent": 5.578,
    "userCpuConsumptionPercent": 3.9843,
    "kernelCpuConsumptionPercent": 1.5937,
    "fsActivity": {
      "reads": 0,
      "writes": 0
    }
  },
  "libuv": [
    {
      "type": "async",
      "is_active": true,
      "is_referenced": false,
      "address": "0x0000000102910900",
      "details": ""
    },
    {
      "type": "timer",
      "is_active": false,
      "is_referenced": false,
      "address": "0x00007fff5fbfeab0",
      "repeat": 0,
      "firesInMsFromNow": 94403548320796,
      "expired": true
    },
    {
      "type": "check",
      "is_active": true,
      "is_referenced": false,
      "address": "0x00007fff5fbfeb48"
    },
    {
      "type": "idle",
      "is_active": false,
      "is_referenced": true,
      "address": "0x00007fff5fbfebc0"
    },
    {
      "type": "prepare",
      "is_active": false,
      "is_referenced": false,
      "address": "0x00007fff5fbfec38"
    },
    {
      "type": "check",
      "is_active": false,
      "is_referenced": false,
      "address": "0x00007fff5fbfecb0"
    },
    {
      "type": "async",
      "is_active": true,
      "is_referenced": false,
      "address": "0x000000010188f2e0"
    },
    {
      "type": "tty",
      "is_active": false,
      "is_referenced": true,
      "address": "0x000055b581db0e18",
      "width": 204,
      "height": 55,
      "fd": 17,
      "writeQueueSize": 0,
      "readable": true,
      "writable": true
    },
    {
      "type": "signal",
      "is_active": true,
      "is_referenced": false,
      "address": "0x000055b581d80010",
      "signum": 28,
      "signal": "SIGWINCH"
    },
    {
      "type": "tty",
      "is_active": true,
      "is_referenced": true,
      "address": "0x000055b581df59f8",
      "width": 204,
      "height": 55,
      "fd": 19,
      "writeQueueSize": 0,
      "readable": true,
      "writable": true
    },
    {
      "type": "loop",
      "is_active": true,
      "address": "0x000055fc7b2cb180",
      "loopIdleTimeSeconds": 22644.8
    },
    {
      "type": "tcp",
      "is_active": true,
      "is_referenced": true,
      "address": "0x000055e70fcb85d8",
      "localEndpoint": {
        "host": "localhost",
        "ip4": "127.0.0.1",
        "port": 48986
      },
      "remoteEndpoint": {
        "host": "localhost",
        "ip4": "127.0.0.1",
        "port": 38573
      },
      "sendBufferSize": 2626560,
      "recvBufferSize": 131072,
      "fd": 24,
      "writeQueueSize": 0,
      "readable": true,
      "writable": true
    }
  ],
  "workers": [],
  "environmentVariables": {
    "REMOTEHOST": "REMOVED",
    "MANPATH": "/opt/rh/devtoolset-3/root/usr/share/man:",
    "XDG_SESSION_ID": "66126",
    "HOSTNAME": "test_machine",
    "HOST": "test_machine",
    "TERM": "xterm-256color",
    "SHELL": "/bin/csh",
    "SSH_CLIENT": "REMOVED",
    "PERL5LIB": "/opt/rh/devtoolset-3/root//usr/lib64/perl5/vendor_perl:/opt/rh/devtoolset-3/root/usr/lib/perl5:/opt/rh/devtoolset-3/root//usr/share/perl5/vendor_perl",
    "OLDPWD": "/home/nodeuser/project/node/src",
    "JAVACONFDIRS": "/opt/rh/devtoolset-3/root/etc/java:/etc/java",
    "SSH_TTY": "/dev/pts/0",
    "PCP_DIR": "/opt/rh/devtoolset-3/root",
    "GROUP": "normaluser",
    "USER": "nodeuser",
    "LD_LIBRARY_PATH": "/opt/rh/devtoolset-3/root/usr/lib64:/opt/rh/devtoolset-3/root/usr/lib",
    "HOSTTYPE": "x86_64-linux",
    "XDG_CONFIG_DIRS": "/opt/rh/devtoolset-3/root/etc/xdg:/etc/xdg",
    "MAIL": "/var/spool/mail/nodeuser",
    "PATH": "/home/nodeuser/project/node:/opt/rh/devtoolset-3/root/usr/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin",
    "PWD": "/home/nodeuser/project/node",
    "LANG": "en_US.UTF-8",
    "PS1": "\\u@\\h : \\[\\e[31m\\]\\w\\[\\e[m\\] >  ",
    "SHLVL": "2",
    "HOME": "/home/nodeuser",
    "OSTYPE": "linux",
    "VENDOR": "unknown",
    "PYTHONPATH": "/opt/rh/devtoolset-3/root/usr/lib64/python2.7/site-packages:/opt/rh/devtoolset-3/root/usr/lib/python2.7/site-packages",
    "MACHTYPE": "x86_64",
    "LOGNAME": "nodeuser",
    "XDG_DATA_DIRS": "/opt/rh/devtoolset-3/root/usr/share:/usr/local/share:/usr/share",
    "LESSOPEN": "||/usr/bin/lesspipe.sh %s",
    "INFOPATH": "/opt/rh/devtoolset-3/root/usr/share/info",
    "XDG_RUNTIME_DIR": "/run/user/50141",
    "_": "./node"
  },
  "userLimits": {
    "core_file_size_blocks": {
      "soft": "",
      "hard": "unlimited"
    },
    "data_seg_size_bytes": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "file_size_blocks": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "max_locked_memory_bytes": {
      "soft": "unlimited",
      "hard": 65536
    },
    "max_memory_size_bytes": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "open_files": {
      "soft": "unlimited",
      "hard": 4096
    },
    "stack_size_bytes": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "cpu_time_seconds": {
      "soft": "unlimited",
      "hard": "unlimited"
    },
    "max_user_processes": {
      "soft": "unlimited",
      "hard": 4127290
    },
    "virtual_memory_bytes": {
      "soft": "unlimited",
      "hard": "unlimited"
    }
  },
  "sharedObjects": [
    "/lib64/libdl.so.2",
    "/lib64/librt.so.1",
    "/lib64/libstdc++.so.6",
    "/lib64/libm.so.6",
    "/lib64/libgcc_s.so.1",
    "/lib64/libpthread.so.0",
    "/lib64/libc.so.6",
    "/lib64/ld-linux-x86-64.so.2"
  ]
}
json

Usage#

node --report-uncaught-exception --report-on-signal \
--report-on-fatalerror app.js
bash
  • --report-uncaught-exception Enables report to be generated on un-caught exceptions. Useful when inspecting JavaScript stack in conjunction with native stack and other runtime environment data.

  • --report-on-signal Enables report to be generated upon receiving the specified (or predefined) signal to the running Node.js process. (See below on how to modify the signal that triggers the report.) Default signal is SIGUSR2. Useful when a report needs to be triggered from another program. Application monitors may leverage this feature to collect report at regular intervals and plot rich set of internal runtime data to their views.

Signal based report generation is not supported in Windows.

Under normal circumstances, there is no need to modify the report triggering signal. However, if SIGUSR2 is already used for other purposes, then this flag helps to change the signal for report generation and preserve the original meaning of SIGUSR2 for the said purposes.

  • --report-on-fatalerror Enables the report to be triggered on fatal errors (internal errors within the Node.js runtime, such as out of memory) that leads to termination of the application. Useful to inspect various diagnostic data elements such as heap, stack, event loop state, resource consumption etc. to reason about the fatal error.

  • --report-on-process-timeout Enables the report to be triggered when the duration set with --process-timeout elapses before the process exits. Useful to reason about why the process did not exit.

  • --report-compact Write reports in a compact format, single-line JSON, more easily consumable by log processing systems than the default multi-line format designed for human consumption.

  • --report-directory Location at which the report will be generated.

  • --report-filename Name of the file to which the report will be written.

  • --report-signal Sets or resets the signal for report generation (not supported on Windows). Default signal is SIGUSR2.

  • --report-exclude-network Exclude header.networkInterfaces and disable the reverse DNS queries in libuv.*.(remote|local)Endpoint.host from the diagnostic report. By default this is not set and the network interfaces are included.

  • --report-exclude-env Exclude environmentVariables from the diagnostic report. By default this is not set and the environment variables are included.

A report can also be triggered via an API call from a JavaScript application:

process.report.writeReport();
js

This function takes an optional additional argument filename, which is the name of a file into which the report is written.

process.report.writeReport('./foo.json');
js

This function takes an optional additional argument err which is an Error object that will be used as the context for the JavaScript stack printed in the report. When using report to handle errors in a callback or an exception handler, this allows the report to include the location of the original error as well as where it was handled.

try {
  process.chdir('/non-existent-path');
} catch (err) {
  process.report.writeReport(err);
}
// Any other code
js

If both filename and error object are passed to writeReport() the error object must be the second parameter.

try {
  process.chdir('/non-existent-path');
} catch (err) {
  process.report.writeReport(filename, err);
}
// Any other code
js

The content of the diagnostic report can be returned as a JavaScript Object via an API call from a JavaScript application:

const report = process.report.getReport();
console.log(typeof report === 'object'); // true

// Similar to process.report.writeReport() output
console.log(JSON.stringify(report, null, 2));
js

This function takes an optional additional argument err, which is an Error object that will be used as the context for the JavaScript stack printed in the report.

const report = process.report.getReport(new Error('custom error'));
console.log(typeof report === 'object'); // true
js

The API versions are useful when inspecting the runtime state from within the application, in expectation of self-adjusting the resource consumption, load balancing, monitoring etc.

The content of the report consists of a header section containing the event type, date, time, PID, and Node.js version, sections containing JavaScript and native stack traces, a section containing V8 heap information, a section containing libuv handle information, and an OS platform information section showing CPU and memory usage and system limits. An example report can be triggered using the Node.js REPL:

$ node
> process.report.writeReport();
Writing Node.js report to file: report.20181126.091102.8480.0.001.json
Node.js report completed
>
console

When a report is written, start and end messages are issued to stderr and the filename of the report is returned to the caller. The default filename includes the date, time, PID, and a sequence number. The sequence number helps in associating the report dump with the runtime state if generated multiple times for the same Node.js process.

Report Version#

Diagnostic report has an associated single-digit version number (report.header.reportVersion), uniquely representing the report format. The version number is bumped when new key is added or removed, or the data type of a value is changed. Report version definitions are consistent across LTS releases.

Version history#

Version 5#

Replace the keys data_seg_size_kbytes, max_memory_size_kbytes, and virtual_memory_kbytes with data_seg_size_bytes, max_memory_size_bytes, and virtual_memory_bytes respectively in the userLimits section, as these values are given in bytes.

{
  "userLimits": {
    // Skip some keys ...
    "data_seg_size_bytes": { // replacing data_seg_size_kbytes
      "soft": "unlimited",
      "hard": "unlimited"
    },
    // ...
    "max_memory_size_bytes": { // replacing max_memory_size_kbytes
      "soft": "unlimited",
      "hard": "unlimited"
    },
    // ...
    "virtual_memory_bytes": { // replacing virtual_memory_kbytes
      "soft": "unlimited",
      "hard": "unlimited"
    }
  }
}
json
Version 4#

New fields ipv4 and ipv6 are added to tcp and udp libuv handles endpoints. Examples:

{
  "libuv": [
    {
      "type": "tcp",
      "is_active": true,
      "is_referenced": true,
      "address": "0x000055e70fcb85d8",
      "localEndpoint": {
        "host": "localhost",
        "ip4": "127.0.0.1", // new key
        "port": 48986
      },
      "remoteEndpoint": {
        "host": "localhost",
        "ip4": "127.0.0.1", // new key
        "port": 38573
      },
      "sendBufferSize": 2626560,
      "recvBufferSize": 131072,
      "fd": 24,
      "writeQueueSize": 0,
      "readable": true,
      "writable": true
    },
    {
      "type": "tcp",
      "is_active": true,
      "is_referenced": true,
      "address": "0x000055e70fcd68c8",
      "localEndpoint": {
        "host": "ip6-localhost",
        "ip6": "::1", // new key
        "port": 52266
      },
      "remoteEndpoint": {
        "host": "ip6-localhost",
        "ip6": "::1", // new key
        "port": 38573
      },
      "sendBufferSize": 2626560,
      "recvBufferSize": 131072,
      "fd": 25,
      "writeQueueSize": 0,
      "readable": false,
      "writable": false
    }
  ]
}
json
Version 3#

The following memory usage keys are added to the resourceUsage section.

{
  "resourceUsage": {
    "rss": "35766272",
    "free_memory": "1598337024",
    "total_memory": "17179869184",
    "available_memory": "1598337024",
    "constrained_memory": "36624662528"
  }
}
json
Version 2#

Added Worker support. Refer to Interaction with workers section for more details.

Version 1#

This is the first version of the diagnostic report.

Configuration#

Additional runtime configuration of report generation is available via the following properties of process.report:

reportOnFatalError triggers diagnostic reporting on fatal errors when true. Defaults to false.

reportOnSignal triggers diagnostic reporting on signal when true. This is not supported on Windows. Defaults to false.

reportOnUncaughtException triggers diagnostic reporting on uncaught exception when true. Defaults to false.

signal specifies the POSIX signal identifier that will be used to intercept external triggers for report generation. Defaults to 'SIGUSR2'.

filename specifies the name of the output file in the file system. Special meaning is attached to stdout and stderr. Usage of these will result in report being written to the associated standard streams. In cases where standard streams are used, the value in directory is ignored. URLs are not supported. Defaults to a composite filename that contains timestamp, PID, and sequence number.

directory specifies the file system directory where the report will be written. URLs are not supported. Defaults to the current working directory of the Node.js process.

excludeNetwork excludes header.networkInterfaces from the diagnostic report.

// Trigger report only on uncaught exceptions.
process.report.reportOnFatalError = false;
process.report.reportOnSignal = false;
process.report.reportOnUncaughtException = true;

// Trigger report for both internal errors as well as external signal.
process.report.reportOnFatalError = true;
process.report.reportOnSignal = true;
process.report.reportOnUncaughtException = false;

// Change the default signal to 'SIGQUIT' and enable it.
process.report.reportOnFatalError = false;
process.report.reportOnUncaughtException = false;
process.report.reportOnSignal = true;
process.report.signal = 'SIGQUIT';

// Disable network interfaces reporting
process.report.excludeNetwork = true;
js

Configuration on module initialization is also available via environment variables:

NODE_OPTIONS="--report-uncaught-exception \
  --report-on-fatalerror --report-on-signal \
  --report-signal=SIGUSR2  --report-filename=./report.json \
  --report-directory=/home/nodeuser"
bash

Specific API documentation can be found under process API documentation section.

Interaction with workers#

Worker threads can create reports in the same way that the main thread does.

Reports will include information on any Workers that are children of the current thread as part of the workers section, with each Worker generating a report in the standard report format.

The thread which is generating the report will wait for the reports from Worker threads to finish. However, the latency for this will usually be low, as both running JavaScript and the event loop are interrupted to generate the report.

Diagnostics Channel#

Stability: 2 - Stable

The node:diagnostics_channel module provides an API to create named channels to report arbitrary message data for diagnostics purposes.

It can be accessed using:

import diagnostics_channel from 'node:diagnostics_channel';
const diagnostics_channel = require('node:diagnostics_channel');
javascript

It is intended that a module writer wanting to report diagnostics messages will create one or many top-level channels to report messages through. Channels may also be acquired at runtime but it is not encouraged due to the additional overhead of doing so. Channels may be exported for convenience, but as long as the name is known it can be acquired anywhere.

If you intend for your module to produce diagnostics data for others to consume it is recommended that you include documentation of what named channels are used along with the shape of the message data. Channel names should generally include the module name to avoid collisions with data from other modules.

Public API#

Overview#

Following is a simple overview of the public API.

import diagnostics_channel from 'node:diagnostics_channel';

// Get a reusable channel object
const channel = diagnostics_channel.channel('my-channel');

function onMessage(message, name) {
  // Received data
}

// Subscribe to the channel
diagnostics_channel.subscribe('my-channel', onMessage);

// Check if the channel has an active subscriber
if (channel.hasSubscribers) {
  // Publish data to the channel
  channel.publish({
    some: 'data',
  });
}

// Unsubscribe from the channel
diagnostics_channel.unsubscribe('my-channel', onMessage);
const diagnostics_channel = require('node:diagnostics_channel');

// Get a reusable channel object
const channel = diagnostics_channel.channel('my-channel');

function onMessage(message, name) {
  // Received data
}

// Subscribe to the channel
diagnostics_channel.subscribe('my-channel', onMessage);

// Check if the channel has an active subscriber
if (channel.hasSubscribers) {
  // Publish data to the channel
  channel.publish({
    some: 'data',
  });
}

// Unsubscribe from the channel
diagnostics_channel.unsubscribe('my-channel', onMessage);
javascript
diagnostics_channel.hasSubscribers(name)#

Check if there are active subscribers to the named channel. This is helpful if the message you want to send might be expensive to prepare.

This API is optional but helpful when trying to publish messages from very performance-sensitive code.

import diagnostics_channel from 'node:diagnostics_channel';

if (diagnostics_channel.hasSubscribers('my-channel')) {
  // There are subscribers, prepare and publish message
}
const diagnostics_channel = require('node:diagnostics_channel');

if (diagnostics_channel.hasSubscribers('my-channel')) {
  // There are subscribers, prepare and publish message
}
javascript
diagnostics_channel.channel(name)#

This is the primary entry-point for anyone wanting to publish to a named channel. It produces a channel object which is optimized to reduce overhead at publish time as much as possible.

import diagnostics_channel from 'node:diagnostics_channel';

const channel = diagnostics_channel.channel('my-channel');
const diagnostics_channel = require('node:diagnostics_channel');

const channel = diagnostics_channel.channel('my-channel');
javascript
diagnostics_channel.subscribe(name, onMessage)#

Register a message handler to subscribe to this channel. This message handler will be run synchronously whenever a message is published to the channel. Any errors thrown in the message handler will trigger an 'uncaughtException'.

import diagnostics_channel from 'node:diagnostics_channel';

diagnostics_channel.subscribe('my-channel', (message, name) => {
  // Received data
});
const diagnostics_channel = require('node:diagnostics_channel');

diagnostics_channel.subscribe('my-channel', (message, name) => {
  // Received data
});
javascript
diagnostics_channel.unsubscribe(name, onMessage)#
  • name <string> | <symbol> The channel name
  • onMessage <Function> The previous subscribed handler to remove
  • Returns: <boolean> true if the handler was found, false otherwise.

Remove a message handler previously registered to this channel with diagnostics_channel.subscribe(name, onMessage).

import diagnostics_channel from 'node:diagnostics_channel';

function onMessage(message, name) {
  // Received data
}

diagnostics_channel.subscribe('my-channel', onMessage);

diagnostics_channel.unsubscribe('my-channel', onMessage);
const diagnostics_channel = require('node:diagnostics_channel');

function onMessage(message, name) {
  // Received data
}

diagnostics_channel.subscribe('my-channel', onMessage);

diagnostics_channel.unsubscribe('my-channel', onMessage);
javascript
diagnostics_channel.tracingChannel(nameOrChannels)#

Stability: 2 - Stable

Creates a TracingChannel wrapper for the given TracingChannel Channels. If a name is given, the corresponding tracing channels will be created in the form of tracing:${name}:${eventType} where eventType corresponds to the types of TracingChannel Channels.

import diagnostics_channel from 'node:diagnostics_channel';

const channelsByName = diagnostics_channel.tracingChannel('my-channel');

// or...

const channelsByCollection = diagnostics_channel.tracingChannel({
  start: diagnostics_channel.channel('tracing:my-channel:start'),
  end: diagnostics_channel.channel('tracing:my-channel:end'),
  asyncStart: diagnostics_channel.channel('tracing:my-channel:asyncStart'),
  asyncEnd: diagnostics_channel.channel('tracing:my-channel:asyncEnd'),
  error: diagnostics_channel.channel('tracing:my-channel:error'),
});
const diagnostics_channel = require('node:diagnostics_channel');

const channelsByName = diagnostics_channel.tracingChannel('my-channel');

// or...

const channelsByCollection = diagnostics_channel.tracingChannel({
  start: diagnostics_channel.channel('tracing:my-channel:start'),
  end: diagnostics_channel.channel('tracing:my-channel:end'),
  asyncStart: diagnostics_channel.channel('tracing:my-channel:asyncStart'),
  asyncEnd: diagnostics_channel.channel('tracing:my-channel:asyncEnd'),
  error: diagnostics_channel.channel('tracing:my-channel:error'),
});
javascript
diagnostics_channel.boundedChannel(nameOrChannels)#

Stability: 1 - Experimental

Creates a BoundedChannel wrapper for the given channels. If a name is given, the corresponding channels will be created in the form of tracing:${name}:${eventType} where eventType is start or end.

A BoundedChannel is a simplified version of TracingChannel that only traces synchronous operations. It only has start and end events, without asyncStart, asyncEnd, or error events, making it suitable for tracing operations that don't involve asynchronous continuations or error handling.

import { boundedChannel, channel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

// or...

const wc2 = boundedChannel({
  start: channel('tracing:my-operation:start'),
  end: channel('tracing:my-operation:end'),
});
const { boundedChannel, channel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

// or...

const wc2 = boundedChannel({
  start: channel('tracing:my-operation:start'),
  end: channel('tracing:my-operation:end'),
});
javascript

Class: Channel#

The class Channel represents an individual named channel within the data pipeline. It is used to track subscribers and to publish messages when there are subscribers present. It exists as a separate object to avoid channel lookups at publish time, enabling very fast publish speeds and allowing for heavy use while incurring very minimal cost. Channels are created with diagnostics_channel.channel(name), constructing a channel directly with new Channel(name) is not supported.

channel.hasSubscribers#
  • Returns: <boolean> If there are active subscribers

Check if there are active subscribers to this channel. This is helpful if the message you want to send might be expensive to prepare.

This API is optional but helpful when trying to publish messages from very performance-sensitive code.

import diagnostics_channel from 'node:diagnostics_channel';

const channel = diagnostics_channel.channel('my-channel');

if (channel.hasSubscribers) {
  // There are subscribers, prepare and publish message
}
const diagnostics_channel = require('node:diagnostics_channel');

const channel = diagnostics_channel.channel('my-channel');

if (channel.hasSubscribers) {
  // There are subscribers, prepare and publish message
}
javascript
channel.publish(message)#
  • message <any> The message to send to the channel subscribers

Publish a message to any subscribers to the channel. This will trigger message handlers synchronously so they will execute within the same context.

import diagnostics_channel from 'node:diagnostics_channel';

const channel = diagnostics_channel.channel('my-channel');

channel.publish({
  some: 'message',
});
const diagnostics_channel = require('node:diagnostics_channel');

const channel = diagnostics_channel.channel('my-channel');

channel.publish({
  some: 'message',
});
javascript
channel.subscribe(onMessage)#

Register a message handler to subscribe to this channel. This message handler will be run synchronously whenever a message is published to the channel. Any errors thrown in the message handler will trigger an 'uncaughtException'.

import diagnostics_channel from 'node:diagnostics_channel';

const channel = diagnostics_channel.channel('my-channel');

channel.subscribe((message, name) => {
  // Received data
});
const diagnostics_channel = require('node:diagnostics_channel');

const channel = diagnostics_channel.channel('my-channel');

channel.subscribe((message, name) => {
  // Received data
});
javascript
channel.unsubscribe(onMessage)#
  • onMessage <Function> The previous subscribed handler to remove
  • Returns: <boolean> true if the handler was found, false otherwise.

Remove a message handler previously registered to this channel with channel.subscribe(onMessage).

import diagnostics_channel from 'node:diagnostics_channel';

const channel = diagnostics_channel.channel('my-channel');

function onMessage(message, name) {
  // Received data
}

channel.subscribe(onMessage);

channel.unsubscribe(onMessage);
const diagnostics_channel = require('node:diagnostics_channel');

const channel = diagnostics_channel.channel('my-channel');

function onMessage(message, name) {
  // Received data
}

channel.subscribe(onMessage);

channel.unsubscribe(onMessage);
javascript
channel.bindStore(store[, transform])#

Stability: 1 - Experimental

  • store <AsyncLocalStorage> The store to which to bind the context data
  • transform <Function> Transform context data before setting the store context

When channel.runStores(context, ...) is called, the given context data will be applied to any store bound to the channel. If the store has already been bound the previous transform function will be replaced with the new one. The transform function may be omitted to set the given context data as the context directly.

import diagnostics_channel from 'node:diagnostics_channel';
import { AsyncLocalStorage } from 'node:async_hooks';

const store = new AsyncLocalStorage();

const channel = diagnostics_channel.channel('my-channel');

channel.bindStore(store, (data) => {
  return { data };
});
const diagnostics_channel = require('node:diagnostics_channel');
const { AsyncLocalStorage } = require('node:async_hooks');

const store = new AsyncLocalStorage();

const channel = diagnostics_channel.channel('my-channel');

channel.bindStore(store, (data) => {
  return { data };
});
javascript
channel.unbindStore(store)#

Stability: 1 - Experimental

Remove a message handler previously registered to this channel with channel.bindStore(store).

import diagnostics_channel from 'node:diagnostics_channel';
import { AsyncLocalStorage } from 'node:async_hooks';

const store = new AsyncLocalStorage();

const channel = diagnostics_channel.channel('my-channel');

channel.bindStore(store);
channel.unbindStore(store);
const diagnostics_channel = require('node:diagnostics_channel');
const { AsyncLocalStorage } = require('node:async_hooks');

const store = new AsyncLocalStorage();

const channel = diagnostics_channel.channel('my-channel');

channel.bindStore(store);
channel.unbindStore(store);
javascript
channel.runStores(context, fn[, thisArg[, ...args]])#

Stability: 1 - Experimental

  • context <any> Message to send to subscribers and bind to stores
  • fn <Function> Handler to run within the entered storage context
  • thisArg <any> The receiver to be used for the function call.
  • ...args <any> Optional arguments to pass to the function.

Applies the given data to any AsyncLocalStorage instances bound to the channel for the duration of the given function, then publishes to the channel within the scope of that data is applied to the stores.

If a transform function was given to channel.bindStore(store) it will be applied to transform the message data before it becomes the context value for the store. The prior storage context is accessible from within the transform function in cases where context linking is required.

The context applied to the store should be accessible in any async code which continues from execution which began during the given function, however there are some situations in which context loss may occur.

import diagnostics_channel from 'node:diagnostics_channel';
import { AsyncLocalStorage } from 'node:async_hooks';

const store = new AsyncLocalStorage();

const channel = diagnostics_channel.channel('my-channel');

channel.bindStore(store, (message) => {
  const parent = store.getStore();
  return new Span(message, parent);
});
channel.runStores({ some: 'message' }, () => {
  store.getStore(); // Span({ some: 'message' })
});
const diagnostics_channel = require('node:diagnostics_channel');
const { AsyncLocalStorage } = require('node:async_hooks');

const store = new AsyncLocalStorage();

const channel = diagnostics_channel.channel('my-channel');

channel.bindStore(store, (message) => {
  const parent = store.getStore();
  return new Span(message, parent);
});
channel.runStores({ some: 'message' }, () => {
  store.getStore(); // Span({ some: 'message' })
});
javascript
channel.withStoreScope(data)#

Stability: 1 - Experimental

Creates a disposable scope that binds the given data to any AsyncLocalStorage instances bound to the channel and publishes it to subscribers. The scope automatically restores the previous storage contexts when disposed.

This method enables the use of JavaScript's explicit resource management (using syntax with Symbol.dispose) to manage store contexts without closure wrapping.

import { channel } from 'node:diagnostics_channel';
import { AsyncLocalStorage } from 'node:async_hooks';

const store = new AsyncLocalStorage();
const ch = channel('my-channel');

ch.bindStore(store, (message) => {
  return { ...message, timestamp: Date.now() };
});

{
  using scope = ch.withStoreScope({ request: 'data' });
  // Store is entered, data is published
  console.log(store.getStore()); // { request: 'data', timestamp: ... }
}
// Store is automatically restored on scope exit
const { channel } = require('node:diagnostics_channel');
const { AsyncLocalStorage } = require('node:async_hooks');

const store = new AsyncLocalStorage();
const ch = channel('my-channel');

ch.bindStore(store, (message) => {
  return { ...message, timestamp: Date.now() };
});

{
  using scope = ch.withStoreScope({ request: 'data' });
  // Store is entered, data is published
  console.log(store.getStore()); // { request: 'data', timestamp: ... }
}
// Store is automatically restored on scope exit
javascript

Class: RunStoresScope#

Stability: 1 - Experimental

The class RunStoresScope represents a disposable scope created by channel.withStoreScope(data). It manages the lifecycle of store contexts and ensures they are properly restored when the scope exits.

The scope must be used with the using syntax to ensure proper disposal.

Class: TracingChannel#

Stability: 2 - Stable

The class TracingChannel is a collection of TracingChannel Channels which together express a single traceable action. It is used to formalize and simplify the process of producing events for tracing application flow. diagnostics_channel.tracingChannel() is used to construct a TracingChannel. As with Channel it is recommended to create and reuse a single TracingChannel at the top-level of the file rather than creating them dynamically.

tracingChannel.subscribe(subscribers)#

Helper to subscribe a collection of functions to the corresponding channels. This is the same as calling channel.subscribe(onMessage) on each channel individually.

import diagnostics_channel from 'node:diagnostics_channel';

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.subscribe({
  start(message) {
    // Handle start message
  },
  end(message) {
    // Handle end message
  },
  asyncStart(message) {
    // Handle asyncStart message
  },
  asyncEnd(message) {
    // Handle asyncEnd message
  },
  error(message) {
    // Handle error message
  },
});
const diagnostics_channel = require('node:diagnostics_channel');

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.subscribe({
  start(message) {
    // Handle start message
  },
  end(message) {
    // Handle end message
  },
  asyncStart(message) {
    // Handle asyncStart message
  },
  asyncEnd(message) {
    // Handle asyncEnd message
  },
  error(message) {
    // Handle error message
  },
});
javascript
tracingChannel.unsubscribe(subscribers)#

Helper to unsubscribe a collection of functions from the corresponding channels. This is the same as calling channel.unsubscribe(onMessage) on each channel individually.

import diagnostics_channel from 'node:diagnostics_channel';

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.unsubscribe({
  start(message) {
    // Handle start message
  },
  end(message) {
    // Handle end message
  },
  asyncStart(message) {
    // Handle asyncStart message
  },
  asyncEnd(message) {
    // Handle asyncEnd message
  },
  error(message) {
    // Handle error message
  },
});
const diagnostics_channel = require('node:diagnostics_channel');

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.unsubscribe({
  start(message) {
    // Handle start message
  },
  end(message) {
    // Handle end message
  },
  asyncStart(message) {
    // Handle asyncStart message
  },
  asyncEnd(message) {
    // Handle asyncEnd message
  },
  error(message) {
    // Handle error message
  },
});
javascript
tracingChannel.traceSync(fn[, context[, thisArg[, ...args]]])#
  • fn <Function> Function to wrap a trace around
  • context <Object> Shared object to correlate events through
  • thisArg <any> The receiver to be used for the function call
  • ...args <any> Optional arguments to pass to the function
  • Returns: <any> The return value of the given function

Trace a synchronous function call. This will always produce a start event and end event around the execution and may produce an error event if the given function throws an error. This will run the given function using channel.runStores(context, ...) on the start channel which ensures all events should have any bound stores set to match this trace context.

To ensure only correct trace graphs are formed, events will only be published if subscribers are present prior to starting the trace. Subscriptions which are added after the trace begins will not receive future events from that trace, only future traces will be seen.

import diagnostics_channel from 'node:diagnostics_channel';

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.traceSync(() => {
  // Do something
}, {
  some: 'thing',
});
const diagnostics_channel = require('node:diagnostics_channel');

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.traceSync(() => {
  // Do something
}, {
  some: 'thing',
});
javascript
tracingChannel.tracePromise(fn[, context[, thisArg[, ...args]]])#
  • fn <Function> Function to wrap a trace around
  • context <Object> Shared object to correlate trace events through
  • thisArg <any> The receiver to be used for the function call
  • ...args <any> Optional arguments to pass to the function
  • Returns: <any> The return value of the given function. If the return value is a Promise or thenable, tracing events will be published when it settles. If the return value is not a Promise or thenable, it is returned as-is and a warning is emitted.

Trace an asynchronous function call which returns a <Promise> or thenable object. This will always produce a start event and end event around the synchronous portion of the function execution, and will produce an asyncStart event and asyncEnd event when the returned promise is resolved or rejected. It may also produce an error event if the given function throws an error or the returned promise is rejected. This will run the given function using channel.runStores(context, ...) on the start channel which ensures all events should have any bound stores set to match this trace context.

If the value returned by fn is not a Promise or thenable, then it will be returned with a warning, and no asyncStart or asyncEnd events will be produced.

To ensure only correct trace graphs are formed, events will only be published if subscribers are present prior to starting the trace. Subscriptions which are added after the trace begins will not receive future events from that trace, only future traces will be seen.

import diagnostics_channel from 'node:diagnostics_channel';

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.tracePromise(async () => {
  // Do something
}, {
  some: 'thing',
});
const diagnostics_channel = require('node:diagnostics_channel');

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.tracePromise(async () => {
  // Do something
}, {
  some: 'thing',
});
javascript
tracingChannel.traceCallback(fn[, position[, context[, thisArg[, ...args]]]])#
  • fn <Function> callback using function to wrap a trace around
  • position <number> Zero-indexed argument position of expected callback (defaults to last argument if undefined is passed)
  • context <Object> Shared object to correlate trace events through (defaults to {} if undefined is passed)
  • thisArg <any> The receiver to be used for the function call
  • ...args <any> arguments to pass to the function (must include the callback)
  • Returns: <any> The return value of the given function

Trace a callback-receiving function call. The callback is expected to follow the error as first arg convention typically used. This will always produce a start event and end event around the synchronous portion of the function execution, and will produce a asyncStart event and asyncEnd event around the callback execution. It may also produce an error event if the given function throws or the first argument passed to the callback is set. This will run the given function using channel.runStores(context, ...) on the start channel which ensures all events should have any bound stores set to match this trace context.

To ensure only correct trace graphs are formed, events will only be published if subscribers are present prior to starting the trace. Subscriptions which are added after the trace begins will not receive future events from that trace, only future traces will be seen.

import diagnostics_channel from 'node:diagnostics_channel';

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.traceCallback((arg1, callback) => {
  // Do something
  callback(null, 'result');
}, 1, {
  some: 'thing',
}, thisArg, arg1, callback);
const diagnostics_channel = require('node:diagnostics_channel');

const channels = diagnostics_channel.tracingChannel('my-channel');

channels.traceCallback((arg1, callback) => {
  // Do something
  callback(null, 'result');
}, 1, {
  some: 'thing',
}, thisArg, arg1, callback);
javascript

The callback will also be run with channel.runStores(context, ...) which enables context loss recovery in some cases.

import diagnostics_channel from 'node:diagnostics_channel';
import { AsyncLocalStorage } from 'node:async_hooks';

const channels = diagnostics_channel.tracingChannel('my-channel');
const myStore = new AsyncLocalStorage();

// The start channel sets the initial store data to something
// and stores that store data value on the trace context object
channels.start.bindStore(myStore, (data) => {
  const span = new Span(data);
  data.span = span;
  return span;
});

// Then asyncStart can restore from that data it stored previously
channels.asyncStart.bindStore(myStore, (data) => {
  return data.span;
});
const diagnostics_channel = require('node:diagnostics_channel');
const { AsyncLocalStorage } = require('node:async_hooks');

const channels = diagnostics_channel.tracingChannel('my-channel');
const myStore = new AsyncLocalStorage();

// The start channel sets the initial store data to something
// and stores that store data value on the trace context object
channels.start.bindStore(myStore, (data) => {
  const span = new Span(data);
  data.span = span;
  return span;
});

// Then asyncStart can restore from that data it stored previously
channels.asyncStart.bindStore(myStore, (data) => {
  return data.span;
});
javascript
tracingChannel.hasSubscribers#
  • Returns: <boolean> true if any of the individual channels has a subscriber, false if not.

This is a helper method available on a TracingChannel instance to check if any of the TracingChannel Channels have subscribers. A true is returned if any of them have at least one subscriber, a false is returned otherwise.

import diagnostics_channel from 'node:diagnostics_channel';

const channels = diagnostics_channel.tracingChannel('my-channel');

if (channels.hasSubscribers) {
  // Do something
}
const diagnostics_channel = require('node:diagnostics_channel');

const channels = diagnostics_channel.tracingChannel('my-channel');

if (channels.hasSubscribers) {
  // Do something
}
javascript

Class: BoundedChannel#

Stability: 1 - Experimental

The class BoundedChannel is a simplified version of TracingChannel that only traces synchronous operations. It consists of two channels (start and end) instead of five, omitting the asyncStart, asyncEnd, and error events. This makes it suitable for tracing operations that don't involve asynchronous continuations or error handling.

Like TracingChannel, it is recommended to create and reuse a single BoundedChannel at the top-level of the file rather than creating them dynamically.

boundedChannel.hasSubscribers#
  • Returns: <boolean> true if any of the individual channels has a subscriber, false if not.

Check if any of the start or end channels have subscribers.

import { boundedChannel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

if (wc.hasSubscribers) {
  // There are subscribers, perform traced operation
}
const { boundedChannel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

if (wc.hasSubscribers) {
  // There are subscribers, perform traced operation
}
javascript
boundedChannel.subscribe(handlers)#

Subscribe to the bounded channel events. This is equivalent to calling channel.subscribe(onMessage) on each channel individually.

import { boundedChannel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

wc.subscribe({
  start(message) {
    // Handle start
  },
  end(message) {
    // Handle end
  },
});
const { boundedChannel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

wc.subscribe({
  start(message) {
    // Handle start
  },
  end(message) {
    // Handle end
  },
});
javascript
boundedChannel.unsubscribe(handlers)#
  • handlers <Object> Set of channel subscribers
  • Returns: <boolean> true if all handlers were successfully unsubscribed, false otherwise.

Unsubscribe from the bounded channel events. This is equivalent to calling channel.unsubscribe(onMessage) on each channel individually.

import { boundedChannel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

const handlers = {
  start(message) {},
  end(message) {},
};

wc.subscribe(handlers);
wc.unsubscribe(handlers);
const { boundedChannel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

const handlers = {
  start(message) {},
  end(message) {},
};

wc.subscribe(handlers);
wc.unsubscribe(handlers);
javascript
boundedChannel.run(context, fn[, thisArg[, ...args]])#
  • context <Object> Shared object to correlate events through
  • fn <Function> Function to wrap a trace around
  • thisArg <any> The receiver to be used for the function call
  • ...args <any> Optional arguments to pass to the function
  • Returns: <any> The return value of the given function

Trace a synchronous function call. This will produce a start event and end event around the execution. This runs the given function using channel.runStores(context, ...) on the start channel which ensures all events have any bound stores set to match this trace context.

import { boundedChannel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

const result = wc.run({ operationId: '123' }, () => {
  // Perform operation
  return 42;
});
const { boundedChannel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

const result = wc.run({ operationId: '123' }, () => {
  // Perform operation
  return 42;
});
javascript
boundedChannel.withScope([context])#

Create a disposable scope for tracing a synchronous operation using JavaScript's explicit resource management (using syntax). The scope automatically publishes start and end events, enters bound stores, and handles cleanup when disposed.

import { boundedChannel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

const context = { operationId: '123' };
{
  using scope = wc.withScope(context);
  // Stores are entered, start event is published

  // Perform work and set result on context
  context.result = 42;
}
// End event is published, stores are restored automatically
const { boundedChannel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

const context = { operationId: '123' };
{
  using scope = wc.withScope(context);
  // Stores are entered, start event is published

  // Perform work and set result on context
  context.result = 42;
}
// End event is published, stores are restored automatically
javascript

Class: BoundedChannelScope#

Stability: 1 - Experimental

The class BoundedChannelScope represents a disposable scope created by boundedChannel.withScope(context). It manages the lifecycle of a traced operation, automatically publishing events and managing store contexts.

The scope must be used with the using syntax to ensure proper disposal.

import { boundedChannel } from 'node:diagnostics_channel';

const wc = boundedChannel('my-operation');

const context = {};
{
  using scope = wc.withScope(context);
  // Start event is published, stores are entered
  context.result = performOperation();
  // End event is automatically published at end of block
}
const { boundedChannel } = require('node:diagnostics_channel');

const wc = boundedChannel('my-operation');

const context = {};
{
  using scope = wc.withScope(context);
  // Start event is published, stores are entered
  context.result = performOperation();
  // End event is automatically published at end of block
}
javascript

BoundedChannel Channels#

A BoundedChannel consists of two diagnostics channels representing the lifecycle of a scope created with the using syntax:

  • tracing:${name}:start - Published when the using statement executes (scope creation)
  • tracing:${name}:end - Published when exiting the block (scope disposal)

When using the using syntax with [boundedChannel.withScope([context])][], the start event is published immediately when the statement executes, and the end event is automatically published when disposal occurs at the end of the block. All events share the same context object, which can be extended with additional properties like result during scope execution.

TracingChannel Channels#

A TracingChannel is a collection of several diagnostics_channels representing specific points in the execution lifecycle of a single traceable action. The behavior is split into five diagnostics_channels consisting of start, end, asyncStart, asyncEnd, and error. A single traceable action will share the same event object between all events, this can be helpful for managing correlation through a weakmap.

These event objects will be extended with result or error values when the task "completes". In the case of a synchronous task the result will be the return value and the error will be anything thrown from the function. With callback-based async functions the result will be the second argument of the callback while the error will either be a thrown error visible in the end event or the first callback argument in either of the asyncStart or asyncEnd events.

To ensure only correct trace graphs are formed, events should only be published if subscribers are present prior to starting the trace. Subscriptions which are added after the trace begins should not receive future events from that trace, only future traces will be seen.

Tracing channels should follow a naming pattern of:

  • tracing:module.class.method:start or tracing:module.function:start
  • tracing:module.class.method:end or tracing:module.function:end
  • tracing:module.class.method:asyncStart or tracing:module.function:asyncStart
  • tracing:module.class.method:asyncEnd or tracing:module.function:asyncEnd
  • tracing:module.class.method:error or tracing:module.function:error
start(event)#
  • Name: tracing:${name}:start

The start event represents the point at which a function is called. At this point the event data may contain function arguments or anything else available at the very start of the execution of the function.

end(event)#
  • Name: tracing:${name}:end

The end event represents the point at which a function call returns a value. In the case of an async function this is when the promise returned not when the function itself makes a return statement internally. At this point, if the traced function was synchronous the result field will be set to the return value of the function. Alternatively, the error field may be present to represent any thrown errors.

It is recommended to listen specifically to the error event to track errors as it may be possible for a traceable action to produce multiple errors. For example, an async task which fails may be started internally before the sync part of the task then throws an error.

asyncStart(event)#
  • Name: tracing:${name}:asyncStart

The asyncStart event represents the callback or continuation of a traceable function being reached. At this point things like callback arguments may be available, or anything else expressing the "result" of the action.

For callbacks-based functions, the first argument of the callback will be assigned to the error field, if not undefined or null, and the second argument will be assigned to the result field.

For promises, the argument to the resolve path will be assigned to result or the argument to the reject path will be assign to error.

It is recommended to listen specifically to the error event to track errors as it may be possible for a traceable action to produce multiple errors. For example, an async task which fails may be started internally before the sync part of the task then throws an error.

asyncEnd(event)#
  • Name: tracing:${name}:asyncEnd

The asyncEnd event represents the callback of an asynchronous function returning. It's not likely event data will change after the asyncStart event, however it may be useful to see the point where the callback completes.

error(event)#
  • Name: tracing:${name}:error

The error event represents any error produced by the traceable function either synchronously or asynchronously. If an error is thrown in the synchronous portion of the traced function the error will be assigned to the error field of the event and the error event will be triggered. If an error is received asynchronously through a callback or promise rejection it will also be assigned to the error field of the event and trigger the error event.

It is possible for a single traceable function call to produce errors multiple times so this should be considered when consuming this event. For example, if another async task is triggered internally which fails and then the sync part of the function then throws and error two error events will be emitted, one for the sync error and one for the async error.

Built-in Channels#

Console#

Stability: 1 - Experimental

Event: 'console.log'#

Emitted when console.log() is called. Receives and array of the arguments passed to console.log().

Event: 'console.info'#

Emitted when console.info() is called. Receives and array of the arguments passed to console.info().

Event: 'console.debug'#

Emitted when console.debug() is called. Receives and array of the arguments passed to console.debug().

Event: 'console.warn'#

Emitted when console.warn() is called. Receives and array of the arguments passed to console.warn().

Event: 'console.error'#

Emitted when console.error() is called. Receives and array of the arguments passed to console.error().

Crypto#

Stability: 1 - Experimental

Event: 'crypto.fips.indicator'#
  • operation <string> The provider-defined operation type.
  • reason <string> The provider-defined description of why the operation is not approved.
  • blocked <boolean> Whether an indicator callback blocked the operation.
  • count <number> The number of matching pending indicator invocations represented by this message.
  • dropped <number> The number of additional indicator invocations dropped before this message was delivered.

Emitted when the OpenSSL FIPS provider used by Node.js detects an operation that is not FIPS approved after the corresponding provider check was relaxed. Such operations are possible when the provider is configured for backwards compatibility. The operation and reason values come from the provider and should be treated as opaque strings rather than stable enumerations.

Start Node.js with --enable-fips-indicator-events to enable this channel. Without the option, subscribing does not install the OpenSSL callback and no messages are published.

Subscribing to the channel is observation-only and never changes the result of an operation. Node.js preserves the result from any native indicator callback installed before Node.js initializes its crypto support. When --force-fips=strict is used, Node.js rejects callback-indicated non-approved operations whether or not indicator events are enabled or the channel has subscribers.

Messages are published asynchronously on the main thread because OpenSSL indicators can originate from Workers or other threads. Only subscriptions on the main thread receive messages. Delivery order relative to the originating operation is not defined, and a message cannot be correlated with a particular call or Worker.

One cryptographic operation can invoke the OpenSSL indicator more than once. Matching pending invocations are coalesced and reflected in count, which does not necessarily represent a number of cryptographic operations. At most 256 distinct messages are queued. Additional invocations are reported in dropped on the first queued message. Queued messages do not keep the event loop active, so this channel is best-effort diagnostics rather than an authoritative audit log.

This channel observes the default OpenSSL library context used by Node.js. It does not observe native addons or other code that uses another OSSL_LIB_CTX or another copy of libcrypto. It is active with OpenSSL 3.4 and later and is not available with BoringSSL. A provider configured to reject a check directly, including a pedantic OpenSSL FIPS provider, can reject an operation without emitting an indicator. Receiving or not receiving a message does not establish that Node.js or a cryptographic operation is FIPS validated.

import diagnosticsChannel from 'node:diagnostics_channel';

diagnosticsChannel.subscribe('crypto.fips.indicator', (message) => {
  console.error('Non-approved cryptographic operation', message);
});
mjs
HTTP#

Stability: 1 - Experimental

Event: 'http.client.request.created'#

Emitted when client creates a request object. Unlike http.client.request.start, this event is emitted before the request has been sent.

Event: 'http.client.request.start'#

Emitted when client starts a request.

Event: 'http.client.request.error'#

Emitted when an error occurs during a client request.

Event: 'http.client.response.finish'#

Emitted when client receives a response.

Event: 'http.server.request.start'#

Emitted when server receives a request.

Event: 'http.server.response.created'#

Emitted when server creates a response. The event is emitted before the response is sent.

Event: 'http.server.response.finish'#

Emitted when server sends a response.

HTTP/2#

Stability: 1 - Experimental

Event: 'http2.client.stream.created'#

Emitted when a stream is created on the client.

Event: 'http2.client.stream.start'#

Emitted when a stream is started on the client.

Event: 'http2.client.stream.error'#

Emitted when an error occurs during the processing of a stream on the client.

Event: 'http2.client.stream.finish'#

Emitted when a stream is received on the client.

Event: 'http2.client.stream.bodyChunkSent'#

Emitted when a chunk of the client stream body is being sent.

Event: 'http2.client.stream.bodySent'#

Emitted after the client stream body has been fully sent.

Event: 'http2.client.stream.close'#

Emitted when a stream is closed on the client. The HTTP/2 error code used when closing the stream can be retrieved using the stream.rstCode property.

Event: 'http2.server.stream.created'#

Emitted when a stream is created on the server.

Event: 'http2.server.stream.start'#

Emitted when a stream is started on the server.

Event: 'http2.server.stream.error'#

Emitted when an error occurs during the processing of a stream on the server.

Event: 'http2.server.stream.finish'#

Emitted when a stream is sent on the server.

Event: 'http2.server.stream.close'#

Emitted when a stream is closed on the server. The HTTP/2 error code used when closing the stream can be retrieved using the stream.rstCode property.

Modules#

Stability: 1 - Experimental

Event: 'tracing:module.require:start'#
  • event <Object> containing the following properties
    • id Argument passed to require(). Module name.
    • parentFilename Name of the module that attempted to require(id).

Emitted when require() is executed. See start event.

Event: 'tracing:module.require:end'#
  • event <Object> containing the following properties
    • id Argument passed to require(). Module name.
    • parentFilename Name of the module that attempted to require(id).

Emitted when a require() call returns. See end event.

Event: 'tracing:module.require:error'#
  • event <Object> containing the following properties
    • id Argument passed to require(). Module name.
    • parentFilename Name of the module that attempted to require(id).
  • error <Error>

Emitted when a require() throws an error. See error event.

Event: 'tracing:module.import:asyncStart'#
  • event <Object> containing the following properties
    • id Argument passed to import(). Module name.
    • parentURL URL object of the module that attempted to import(id).

Emitted when import() is invoked. See asyncStart event.

Event: 'tracing:module.import:asyncEnd'#
  • event <Object> containing the following properties
    • id Argument passed to import(). Module name.
    • parentURL URL object of the module that attempted to import(id).

Emitted when import() has completed. See asyncEnd event.

Event: 'tracing:module.import:error'#
  • event <Object> containing the following properties
    • id Argument passed to import(). Module name.
    • parentURL URL object of the module that attempted to import(id).
  • error <Error>

Emitted when a import() throws an error. See error event.

NET#

Stability: 1 - Experimental

Event: 'net.client.socket'#

Emitted when a new TCP or pipe client socket connection is created.

Event: 'net.server.socket'#

Emitted when a new TCP or pipe connection is received.

Event: 'tracing:net.server.listen:asyncStart'#

Emitted when net.Server.listen() is invoked, before the port or pipe is actually setup.

Event: 'tracing:net.server.listen:asyncEnd'#

Emitted when net.Server.listen() has completed and thus the server is ready to accept connection.

Event: 'tracing:net.server.listen:error'#

Emitted when net.Server.listen() is returning an error.

UDP#

Stability: 1 - Experimental

Event: 'udp.socket'#

Emitted when a new UDP socket is created.

Process#

Stability: 1 - Experimental

Event: 'child_process'#

Emitted when a new process is created.

tracing:child_process.spawn:start

Emitted when child_process.spawn() is invoked, before the process is actually spawned.

tracing:child_process.spawn:end

Emitted when child_process.spawn() has completed successfully and the process has been created.

tracing:child_process.spawn:error

Emitted when child_process.spawn() encounters an error.

Event: 'process.execve'#

Emitted when process.execve() is invoked.

Web Locks#

Stability: 1 - Experimental

These channels are emitted for each locks.request() call. See worker_threads.locks for details on Web Locks.

Event: 'locks.request.start'#
  • name <string> The name of the requested lock resource.
  • mode <string> The lock mode: 'exclusive' or 'shared'.

Emitted when a lock request is initiated, before the lock is granted.

Event: 'locks.request.grant'#
  • name <string> The name of the requested lock resource.
  • mode <string> The lock mode: 'exclusive' or 'shared'.

Emitted when a lock is successfully granted and the callback is about to run.

Event: 'locks.request.miss'#
  • name <string> The name of the requested lock resource.
  • mode <string> The lock mode: 'exclusive' or 'shared'.

Emitted when ifAvailable is true and the lock is not immediately available, and the request callback is invoked with null instead of a Lock object.

Event: 'locks.request.end'#
  • name <string> The name of the requested lock resource.
  • mode <string> The lock mode: 'exclusive' or 'shared'.
  • steal <boolean> Whether the request uses steal semantics.
  • ifAvailable <boolean> Whether the request uses ifAvailable semantics.
  • error <Error> | <undefined> The error thrown by the callback, if any.

Emitted when a lock request has finished, whether the callback succeeded, threw an error, or the lock was stolen.

Worker Thread#

Stability: 1 - Experimental

Event: 'worker_threads'#

Emitted when a new thread is created.

SQLite#

Stability: 1 - Experimental

Event: 'sqlite.db.query'#
  • sql <string> The expanded SQL with bound parameter values substituted. If expansion fails, the source SQL with unsubstituted placeholders is used instead.
  • database <Database> The Database instance that executed the statement.
  • duration <number> SQLite's internal estimate of the statement run time in nanoseconds. This reflects C-layer execution time only and does not include JavaScript binding overhead such as argument marshaling or result-row construction.

Emitted after a SQL statement finishes executing against a Database instance. This is a profiling event: it fires once per statement upon completion and reports an estimated duration from SQLite's internal profiler. It is not a distributed-tracing span. There is no corresponding start event, no async context propagation, and no parent-span linkage. If you need OpenTelemetry-compatible spans or async context propagation, wrap your SQLite calls with a TracingChannel at the JavaScript layer instead.

Publishing is zero-overhead when there are no subscribers.

No event is emitted for a statement that is abandoned mid-iteration and later finalized, either explicitly through statement.close() or when the statement is garbage collected. Subscribers must not close the database or the statement, since both are still in use while the event is being delivered; see database.close() and statement.close().

DNS#

Stability: 2 - Stable

The node:dns module enables name resolution. For example, use it to look up IP addresses of host names.

Although named for the Domain Name System (DNS), it does not always use the DNS protocol for lookups. dns.lookup() uses the operating system facilities to perform name resolution. It may not need to perform any network communication. To perform name resolution the way other applications on the same system do, use dns.lookup().

import dns from 'node:dns';

dns.lookup('example.org', (err, address, family) => {
  console.log('address: %j family: IPv%s', address, family);
});
// address: "2606:2800:21f:cb07:6820:80da:af6b:8b2c" family: IPv6
const dns = require('node:dns');

dns.lookup('example.org', (err, address, family) => {
  console.log('address: %j family: IPv%s', address, family);
});
// address: "2606:2800:21f:cb07:6820:80da:af6b:8b2c" family: IPv6
javascript

All other functions in the node:dns module connect to an actual DNS server to perform name resolution. They will always use the network to perform DNS queries. These functions do not use the same set of configuration files used by dns.lookup() (e.g. /etc/hosts). Use these functions to always perform DNS queries, bypassing other name-resolution facilities.

import dns from 'node:dns';

dns.resolve4('archive.org', (err, addresses) => {
  if (err) throw err;

  console.log(`addresses: ${JSON.stringify(addresses)}`);

  addresses.forEach((a) => {
    dns.reverse(a, (err, hostnames) => {
      if (err) {
        throw err;
      }
      console.log(`reverse for ${a}: ${JSON.stringify(hostnames)}`);
    });
  });
});
const dns = require('node:dns');

dns.resolve4('archive.org', (err, addresses) => {
  if (err) throw err;

  console.log(`addresses: ${JSON.stringify(addresses)}`);

  addresses.forEach((a) => {
    dns.reverse(a, (err, hostnames) => {
      if (err) {
        throw err;
      }
      console.log(`reverse for ${a}: ${JSON.stringify(hostnames)}`);
    });
  });
});
javascript

See the Implementation considerations section for more information.

Class: dns.Resolver#

An independent resolver for DNS requests.

Creating a new resolver uses the default server settings. Setting the servers used for a resolver using resolver.setServers() does not affect other resolvers:

import { Resolver } from 'node:dns';
const resolver = new Resolver();
resolver.setServers(['4.4.4.4']);

// This request will use the server at 4.4.4.4, independent of global settings.
resolver.resolve4('example.org', (err, addresses) => {
  // ...
});
const { Resolver } = require('node:dns');
const resolver = new Resolver();
resolver.setServers(['4.4.4.4']);

// This request will use the server at 4.4.4.4, independent of global settings.
resolver.resolve4('example.org', (err, addresses) => {
  // ...
});
javascript

The following methods from the node:dns module are available:

Resolver([options])#

Create a new resolver.

  • options <Object>
    • timeout <integer> Query timeout in milliseconds, or -1 to use the default timeout.
    • tries <integer> The number of tries the resolver will try contacting each name server before giving up. Default: 4
    • maxTimeout <integer> The max retry timeout, in milliseconds. Default: 0, disabled.

resolver.cancel()#

Cancel all outstanding DNS queries made by this resolver. The corresponding callbacks will be called with an error with code ECANCELLED.

resolver.setLocalAddress([ipv4][, ipv6])#

  • ipv4 <string> A string representation of an IPv4 address. Default: '0.0.0.0'
  • ipv6 <string> A string representation of an IPv6 address. Default: '::0'

The resolver instance will send its requests from the specified IP address. This allows programs to specify outbound interfaces when used on multi-homed systems.

If a v4 or v6 address is not specified, it is set to the default and the operating system will choose a local address automatically.

The resolver will use the v4 local address when making requests to IPv4 DNS servers, and the v6 local address when making requests to IPv6 DNS servers. The rrtype of resolution requests has no impact on the local address used.

dns.getServers()#

Returns an array of IP address strings, formatted according to RFC 5952, that are currently configured for DNS resolution. A string will include a port section if a custom port is used.

[
  "8.8.8.8",
  "2001:4860:4860::8888",
  "8.8.8.8:1053",
  "[2001:4860:4860::8888]:1053",
]
json

dns.lookup(hostname[, options], callback)#

  • hostname <string>
  • options <integer> | <Object>
    • family <integer> | <string> The record family. Must be 4, 6, or 0. For backward compatibility reasons,'IPv4' and 'IPv6' are interpreted as 4 and 6 respectively. The value 0 indicates that either an IPv4 or IPv6 address is returned. If the value 0 is used with { all: true } (see below), either one of or both IPv4 and IPv6 addresses are returned, depending on the system's DNS resolver. Default: 0.
    • hints <number> One or more supported getaddrinfo flags. Multiple flags may be passed by bitwise ORing their values.
    • all <boolean> When true, the callback returns all resolved addresses in an array. Otherwise, returns a single address. Default: false.
    • order <string> When verbatim, the resolved addresses are returned unsorted. When ipv4first, the resolved addresses are sorted by placing IPv4 addresses before IPv6 addresses. When ipv6first, the resolved addresses are sorted by placing IPv6 addresses before IPv4 addresses. Default: verbatim (addresses are not reordered). Default value is configurable using dns.setDefaultResultOrder() or --dns-result-order.
    • verbatim <boolean> When true, the callback receives IPv4 and IPv6 addresses in the order the DNS resolver returned them. When false, IPv4 addresses are placed before IPv6 addresses. This option will be deprecated in favor of order. When both are specified, order has higher precedence. New code should only use order. Default: true (addresses are not reordered). Default value is configurable using dns.setDefaultResultOrder() or --dns-result-order.
  • callback <Function>
    • err <Error>
    • address <string> A string representation of an IPv4 or IPv6 address. Not provided when options.all is true.
    • family <integer> 4 or 6, denoting the family of address, or 0 if the address is not an IPv4 or IPv6 address. 0 is a likely indicator of a bug in the name resolution service used by the operating system. Not provided when options.all is true.
    • addresses <Object>[] An array of address objects when options.all is true. Each object has the following properties:
      • address <string> A string representation of an IPv4 or IPv6 address.
      • family <integer> 4 or 6, denoting the family of address.

Resolves a host name (e.g. 'nodejs.org') into the first found A (IPv4) or AAAA (IPv6) record. All option properties are optional. If options is an integer, then it must be 4 or 6 – if options is not provided, then either IPv4 or IPv6 addresses, or both, are returned if found.

With the all option set to true, the arguments for callback change to (err, addresses), with addresses being an array of objects with the properties address and family.

On error, err is an Error object, where err.code is the error code. Keep in mind that err.code will be set to 'ENOTFOUND' not only when the host name does not exist but also when the lookup fails in other ways such as no available file descriptors.

dns.lookup() does not necessarily have anything to do with the DNS protocol. The implementation uses an operating system facility that can associate names with addresses and vice versa. This implementation can have subtle but important consequences on the behavior of any Node.js program. Please take some time to consult the Implementation considerations section before using dns.lookup().

Example usage:

import dns from 'node:dns';
const options = {
  family: 6,
  hints: dns.ADDRCONFIG | dns.V4MAPPED,
};
dns.lookup('example.org', options, (err, address, family) =>
  console.log('address: %j family: IPv%s', address, family));
// address: "2606:2800:21f:cb07:6820:80da:af6b:8b2c" family: IPv6

// When options.all is true, the result will be an Array.
options.all = true;
dns.lookup('example.org', options, (err, addresses) =>
  console.log('addresses: %j', addresses));
// addresses: [{"address":"2606:2800:21f:cb07:6820:80da:af6b:8b2c","family":6}]
const dns = require('node:dns');
const options = {
  family: 6,
  hints: dns.ADDRCONFIG | dns.V4MAPPED,
};
dns.lookup('example.org', options, (err, address, family) =>
  console.log('address: %j family: IPv%s', address, family));
// address: "2606:2800:21f:cb07:6820:80da:af6b:8b2c" family: IPv6

// When options.all is true, the result will be an Array.
options.all = true;
dns.lookup('example.org', options, (err, addresses) =>
  console.log('addresses: %j', addresses));
// addresses: [{"address":"2606:2800:21f:cb07:6820:80da:af6b:8b2c","family":6}]
javascript

If this method is invoked as its util.promisify()ed version, and all is not set to true, it returns a Promise for an Object with address and family properties.

Supported getaddrinfo flags#

The following flags can be passed as hints to dns.lookup().

  • dns.ADDRCONFIG: Limits returned address types to the types of non-loopback addresses configured on the system. For example, IPv4 addresses are only returned if the current system has at least one IPv4 address configured.
  • dns.V4MAPPED: If the IPv6 family was specified, but no IPv6 addresses were found, then return IPv4 mapped IPv6 addresses. It is not supported on some operating systems (e.g. FreeBSD 10.1).
  • dns.ALL: If dns.V4MAPPED is specified, return resolved IPv6 addresses as well as IPv4 mapped IPv6 addresses.

dns.lookupService(address, port, callback)#

Resolves the given address and port into a host name and service using the operating system's underlying getnameinfo implementation.

If address is not a valid IP address, a TypeError will be thrown. The port will be coerced to a number. If it is not a legal port, a TypeError will be thrown.

On an error, err is an Error object, where err.code is the error code.

import dns from 'node:dns';
dns.lookupService('127.0.0.1', 22, (err, hostname, service) => {
  console.log(hostname, service);
  // Prints: localhost ssh
});
const dns = require('node:dns');
dns.lookupService('127.0.0.1', 22, (err, hostname, service) => {
  console.log(hostname, service);
  // Prints: localhost ssh
});
javascript

If this method is invoked as its util.promisify()ed version, it returns a Promise for an Object with hostname and service properties.

dns.resolve(hostname[, rrtype], callback)#

Uses the DNS protocol to resolve a host name (e.g. 'nodejs.org') into an array of the resource records. The callback function has arguments (err, records). When successful, records will be an array of resource records. The type and structure of individual results varies based on rrtype:

rrtype records contains Result type Shorthand method
'A' IPv4 addresses (default) <string> dns.resolve4()
'AAAA' IPv6 addresses <string> dns.resolve6()
'ANY' any records <Object> dns.resolveAny()
'CAA' CA authorization records <Object> dns.resolveCaa()
'CNAME' canonical name records <string> dns.resolveCname()
'MX' mail exchange records <Object> dns.resolveMx()
'NAPTR' name authority pointer records <Object> dns.resolveNaptr()
'NS' name server records <string> dns.resolveNs()
'PTR' pointer records <string> dns.resolvePtr()
'SOA' start of authority records <Object> dns.resolveSoa()
'SRV' service records <Object> dns.resolveSrv()
'TLSA' certificate associations <Object> dns.resolveTlsa()
'TXT' text records <string>[] dns.resolveTxt()

On error, err is an Error object, where err.code is one of the DNS error codes.

dns.resolve4(hostname[, options], callback)#

  • hostname <string> Host name to resolve.
  • options <Object>
    • ttl <boolean> Retrieves the Time-To-Live value (TTL) of each record. When true, the callback receives an array of { address: '1.2.3.4', ttl: 60 } objects rather than an array of strings, with the TTL expressed in seconds.
  • callback <Function>

Uses the DNS protocol to resolve an IPv4 addresses (A records) for the hostname. The addresses argument passed to the callback function will contain an array of IPv4 addresses (e.g. ['74.125.79.104', '74.125.79.105', '74.125.79.106']).

dns.resolve6(hostname[, options], callback)#

  • hostname <string> Host name to resolve.
  • options <Object>
    • ttl <boolean> Retrieve the Time-To-Live value (TTL) of each record. When true, the callback receives an array of { address: '0:1:2:3:4:5:6:7', ttl: 60 } objects rather than an array of strings, with the TTL expressed in seconds.
  • callback <Function>

Uses the DNS protocol to resolve IPv6 addresses (AAAA records) for the hostname. The addresses argument passed to the callback function will contain an array of IPv6 addresses.

dns.resolveAny(hostname, callback)#

Uses the DNS protocol to resolve all records (also known as ANY or * query). The ret argument passed to the callback function will be an array containing various types of records. Each object has a property type that indicates the type of the current record. And depending on the type, additional properties will be present on the object:

Type Properties
'A' address/ttl
'AAAA' address/ttl
'CAA' Refer to dns.resolveCaa()
'CNAME' value
'MX' Refer to dns.resolveMx()
'NAPTR' Refer to dns.resolveNaptr()
'NS' value
'PTR' value
'SOA' Refer to dns.resolveSoa()
'SRV' Refer to dns.resolveSrv()
'TLSA' Refer to dns.resolveTlsa()
'TXT' This type of record contains an array property called entries which refers to dns.resolveTxt(), e.g. { entries: ['...'], type: 'TXT' }

Here is an example of the ret object passed to the callback:

[ { type: 'A', address: '127.0.0.1', ttl: 299 },
  { type: 'CNAME', value: 'example.com' },
  { type: 'MX', exchange: 'alt4.aspmx.l.example.com', priority: 50 },
  { type: 'NS', value: 'ns1.example.com' },
  { type: 'TXT', entries: [ 'v=spf1 include:_spf.example.com ~all' ] },
  { type: 'SOA',
    nsname: 'ns1.example.com',
    hostmaster: 'admin.example.com',
    serial: 156696742,
    refresh: 900,
    retry: 900,
    expire: 1800,
    minttl: 60 } ];
js

DNS server operators may choose not to respond to ANY queries. It may be better to call individual methods like dns.resolve4(), dns.resolveMx(), and so on. For more details, see RFC 8482.

dns.resolveCname(hostname, callback)#

Uses the DNS protocol to resolve CNAME records for the hostname. The addresses argument passed to the callback function will contain an array of canonical name records available for the hostname (e.g. ['bar.example.com']).

dns.resolveCaa(hostname, callback)#

Uses the DNS protocol to resolve CAA records for the hostname. The addresses argument passed to the callback function will contain an array of certification authority authorization records available for the hostname (e.g. [{critical: 0, iodef: 'mailto:pki@example.com'}, {critical: 128, issue: 'pki.example.com'}]).

dns.resolveMx(hostname, callback)#

Uses the DNS protocol to resolve mail exchange records (MX records) for the hostname. The addresses argument passed to the callback function will contain an array of objects containing both a priority and exchange property (e.g. [{priority: 10, exchange: 'mx.example.com'}, ...]).

dns.resolveNaptr(hostname, callback)#

Uses the DNS protocol to resolve regular expression-based records (NAPTR records) for the hostname. The addresses argument passed to the callback function will contain an array of objects with the following properties:

  • flags
  • service
  • regexp
  • replacement
  • order
  • preference
({
  flags: 's',
  service: 'SIP+D2U',
  regexp: '',
  replacement: '_sip._udp.example.com',
  order: 30,
  preference: 100,
});
js

dns.resolveNs(hostname, callback)#

Uses the DNS protocol to resolve name server records (NS records) for the hostname. The addresses argument passed to the callback function will contain an array of name server records available for hostname (e.g. ['ns1.example.com', 'ns2.example.com']).

dns.resolvePtr(hostname, callback)#

Uses the DNS protocol to resolve pointer records (PTR records) for the hostname. The addresses argument passed to the callback function will be an array of strings containing the reply records.

dns.resolveSoa(hostname, callback)#

Uses the DNS protocol to resolve a start of authority record (SOA record) for the hostname. The address argument passed to the callback function will be an object with the following properties:

  • nsname
  • hostmaster
  • serial
  • refresh
  • retry
  • expire
  • minttl
({
  nsname: 'ns.example.com',
  hostmaster: 'root.example.com',
  serial: 2013101809,
  refresh: 10000,
  retry: 2400,
  expire: 604800,
  minttl: 3600,
});
js

dns.resolveSrv(hostname, callback)#

Uses the DNS protocol to resolve service records (SRV records) for the hostname. The addresses argument passed to the callback function will be an array of objects with the following properties:

  • priority
  • weight
  • port
  • name
({
  priority: 10,
  weight: 5,
  port: 21223,
  name: 'service.example.com',
});
js

dns.resolveTlsa(hostname, callback)#

Uses the DNS protocol to resolve certificate associations (TLSA records) for the hostname. The records argument passed to the callback function is an array of objects with these properties:

  • certUsage
  • selector
  • match
  • data
({
  certUsage: 3,
  selector: 1,
  match: 1,
  data: [ArrayBuffer],
});
js

dns.resolveTxt(hostname, callback)#

Uses the DNS protocol to resolve text queries (TXT records) for the hostname. The records argument passed to the callback function is a two-dimensional array of the text records available for hostname (e.g. [ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]). Each sub-array contains TXT chunks of one record. Depending on the use case, these could be either joined together or treated separately.

dns.reverse(ip, callback)#

Performs a reverse DNS query that resolves an IPv4 or IPv6 address to an array of host names.

On error, err is an Error object, where err.code is one of the DNS error codes.

dns.setDefaultResultOrder(order)#

  • order <string> must be 'ipv4first', 'ipv6first' or 'verbatim'.

Set the default value of order in dns.lookup() and dnsPromises.lookup(). The value could be:

  • ipv4first: sets default order to ipv4first.
  • ipv6first: sets default order to ipv6first.
  • verbatim: sets default order to verbatim.

The default is verbatim and dns.setDefaultResultOrder() have higher priority than --dns-result-order. When using worker threads, dns.setDefaultResultOrder() from the main thread won't affect the default dns orders in workers.

dns.getDefaultResultOrder()#

Get the default value for order in dns.lookup() and dnsPromises.lookup(). The value could be:

  • ipv4first: for order defaulting to ipv4first.
  • ipv6first: for order defaulting to ipv6first.
  • verbatim: for order defaulting to verbatim.

dns.setServers(servers)#

Sets the IP address and port of servers to be used when performing DNS resolution. The servers argument is an array of RFC 5952 formatted addresses. If the port is the IANA default DNS port (53) it can be omitted.

dns.setServers([
  '8.8.8.8',
  '[2001:4860:4860::8888]',
  '8.8.8.8:1053',
  '[2001:4860:4860::8888]:1053',
]);
js

An error will be thrown if an invalid address is provided.

The dns.setServers() method must not be called while a DNS query is in progress.

The dns.setServers() method affects only dns.resolve(), dns.resolve*() and dns.reverse() (and specifically not dns.lookup()).

This method works much like resolve.conf. That is, if attempting to resolve with the first server provided results in a NOTFOUND error, the resolve() method will not attempt to resolve with subsequent servers provided. Fallback DNS servers will only be used if the earlier ones time out or result in some other error.

DNS promises API#

The dns.promises API provides an alternative set of asynchronous DNS methods that return Promise objects rather than using callbacks. The API is accessible via require('node:dns').promises or require('node:dns/promises').

Class: dnsPromises.Resolver#

An independent resolver for DNS requests.

Creating a new resolver uses the default server settings. Setting the servers used for a resolver using resolver.setServers() does not affect other resolvers:

import { Resolver } from 'node:dns/promises';
const resolver = new Resolver();
resolver.setServers(['4.4.4.4']);

// This request will use the server at 4.4.4.4, independent of global settings.
const addresses = await resolver.resolve4('example.org');
const { Resolver } = require('node:dns').promises;
const resolver = new Resolver();
resolver.setServers(['4.4.4.4']);

// This request will use the server at 4.4.4.4, independent of global settings.
resolver.resolve4('example.org').then((addresses) => {
  // ...
});

// Alternatively, the same code can be written using async-await style.
(async function() {
  const addresses = await resolver.resolve4('example.org');
})();
javascript

The following methods from the dnsPromises API are available:

resolver.cancel()#

Cancel all outstanding DNS queries made by this resolver. The corresponding promises will be rejected with an error with the code ECANCELLED.

dnsPromises.getServers()#

Returns an array of IP address strings, formatted according to RFC 5952, that are currently configured for DNS resolution. A string will include a port section if a custom port is used.

[
  "8.8.8.8",
  "2001:4860:4860::8888",
  "8.8.8.8:1053",
  "[2001:4860:4860::8888]:1053"
]
json

dnsPromises.lookup(hostname[, options])#

  • hostname <string>
  • options <integer> | <Object>
    • family <integer> The record family. Must be 4, 6, or 0. The value 0 indicates that either an IPv4 or IPv6 address is returned. If the value 0 is used with { all: true } (see below), either one of or both IPv4 and IPv6 addresses are returned, depending on the system's DNS resolver. Default: 0.
    • hints <number> One or more supported getaddrinfo flags. Multiple flags may be passed by bitwise ORing their values.
    • all <boolean> When true, the Promise is resolved with all addresses in an array. Otherwise, returns a single address. Default: false.
    • order <string> When verbatim, the Promise is resolved with IPv4 and IPv6 addresses in the order the DNS resolver returned them. When ipv4first, IPv4 addresses are placed before IPv6 addresses. When ipv6first, IPv6 addresses are placed before IPv4 addresses. Default: verbatim (addresses are not reordered). Default value is configurable using dns.setDefaultResultOrder() or --dns-result-order. New code should use { order: 'verbatim' }.
    • verbatim <boolean> When true, the Promise is resolved with IPv4 and IPv6 addresses in the order the DNS resolver returned them. When false, IPv4 addresses are placed before IPv6 addresses. This option will be deprecated in favor of order. When both are specified, order has higher precedence. New code should only use order. Default: true (addresses are not reordered). Default value is configurable using dns.setDefaultResultOrder() or --dns-result-order.

Resolves a host name (e.g. 'nodejs.org') into the first found A (IPv4) or AAAA (IPv6) record. All option properties are optional. If options is an integer, then it must be 4 or 6 – if options is not provided, then either IPv4 or IPv6 addresses, or both, are returned if found.

With the all option set to true, the Promise is resolved with addresses being an array of objects with the properties address and family.

On error, the Promise is rejected with an Error object, where err.code is the error code. Keep in mind that err.code will be set to 'ENOTFOUND' not only when the host name does not exist but also when the lookup fails in other ways such as no available file descriptors.

dnsPromises.lookup() does not necessarily have anything to do with the DNS protocol. The implementation uses an operating system facility that can associate names with addresses and vice versa. This implementation can have subtle but important consequences on the behavior of any Node.js program. Please take some time to consult the Implementation considerations section before using dnsPromises.lookup().

Example usage:

import dns from 'node:dns';
const dnsPromises = dns.promises;
const options = {
  family: 6,
  hints: dns.ADDRCONFIG | dns.V4MAPPED,
};

await dnsPromises.lookup('example.org', options).then((result) => {
  console.log('address: %j family: IPv%s', result.address, result.family);
  // address: "2606:2800:21f:cb07:6820:80da:af6b:8b2c" family: IPv6
});

// When options.all is true, the result will be an Array.
options.all = true;
await dnsPromises.lookup('example.org', options).then((result) => {
  console.log('addresses: %j', result);
  // addresses: [{"address":"2606:2800:21f:cb07:6820:80da:af6b:8b2c","family":6}]
});
const dns = require('node:dns');
const dnsPromises = dns.promises;
const options = {
  family: 6,
  hints: dns.ADDRCONFIG | dns.V4MAPPED,
};

dnsPromises.lookup('example.org', options).then((result) => {
  console.log('address: %j family: IPv%s', result.address, result.family);
  // address: "2606:2800:21f:cb07:6820:80da:af6b:8b2c" family: IPv6
});

// When options.all is true, the result will be an Array.
options.all = true;
dnsPromises.lookup('example.org', options).then((result) => {
  console.log('addresses: %j', result);
  // addresses: [{"address":"2606:2800:21f:cb07:6820:80da:af6b:8b2c","family":6}]
});
javascript

dnsPromises.lookupService(address, port)#

Resolves the given address and port into a host name and service using the operating system's underlying getnameinfo implementation.

If address is not a valid IP address, a TypeError will be thrown. The port will be coerced to a number. If it is not a legal port, a TypeError will be thrown.

On error, the Promise is rejected with an Error object, where err.code is the error code.

import dnsPromises from 'node:dns/promises';
const result = await dnsPromises.lookupService('127.0.0.1', 22);

console.log(result.hostname, result.service); // Prints: localhost ssh
const dnsPromises = require('node:dns').promises;
dnsPromises.lookupService('127.0.0.1', 22).then((result) => {
  console.log(result.hostname, result.service);
  // Prints: localhost ssh
});
javascript

dnsPromises.resolve(hostname[, rrtype])#

  • hostname <string> Host name to resolve.
  • rrtype <string> Resource record type. Default: 'A'.

Uses the DNS protocol to resolve a host name (e.g. 'nodejs.org') into an array of the resource records. When successful, the Promise is resolved with an array of resource records. The type and structure of individual results vary based on rrtype:

rrtype records contains Result type Shorthand method
'A' IPv4 addresses (default) <string> dnsPromises.resolve4()
'AAAA' IPv6 addresses <string> dnsPromises.resolve6()
'ANY' any records <Object> dnsPromises.resolveAny()
'CAA' CA authorization records <Object> dnsPromises.resolveCaa()
'CNAME' canonical name records <string> dnsPromises.resolveCname()
'MX' mail exchange records <Object> dnsPromises.resolveMx()
'NAPTR' name authority pointer records <Object> dnsPromises.resolveNaptr()
'NS' name server records <string> dnsPromises.resolveNs()
'PTR' pointer records <string> dnsPromises.resolvePtr()
'SOA' start of authority records <Object> dnsPromises.resolveSoa()
'SRV' service records <Object> dnsPromises.resolveSrv()
'TLSA' certificate associations <Object> dnsPromises.resolveTlsa()
'TXT' text records <string>[] dnsPromises.resolveTxt()

On error, the Promise is rejected with an Error object, where err.code is one of the DNS error codes.

dnsPromises.resolve4(hostname[, options])#

  • hostname <string> Host name to resolve.
  • options <Object>
    • ttl <boolean> Retrieve the Time-To-Live value (TTL) of each record. When true, the Promise is resolved with an array of { address: '1.2.3.4', ttl: 60 } objects rather than an array of strings, with the TTL expressed in seconds.

Uses the DNS protocol to resolve IPv4 addresses (A records) for the hostname. On success, the Promise is resolved with an array of IPv4 addresses (e.g. ['74.125.79.104', '74.125.79.105', '74.125.79.106']).

dnsPromises.resolve6(hostname[, options])#

  • hostname <string> Host name to resolve.
  • options <Object>
    • ttl <boolean> Retrieve the Time-To-Live value (TTL) of each record. When true, the Promise is resolved with an array of { address: '0:1:2:3:4:5:6:7', ttl: 60 } objects rather than an array of strings, with the TTL expressed in seconds.

Uses the DNS protocol to resolve IPv6 addresses (AAAA records) for the hostname. On success, the Promise is resolved with an array of IPv6 addresses.

dnsPromises.resolveAny(hostname)#

Uses the DNS protocol to resolve all records (also known as ANY or * query). On success, the Promise is resolved with an array containing various types of records. Each object has a property type that indicates the type of the current record. And depending on the type, additional properties will be present on the object:

Type Properties
'A' address/ttl
'AAAA' address/ttl
'CAA' Refer to dnsPromises.resolveCaa()
'CNAME' value
'MX' Refer to dnsPromises.resolveMx()
'NAPTR' Refer to dnsPromises.resolveNaptr()
'NS' value
'PTR' value
'SOA' Refer to dnsPromises.resolveSoa()
'SRV' Refer to dnsPromises.resolveSrv()
'TLSA' Refer to dnsPromises.resolveTlsa()
'TXT' This type of record contains an array property called entries which refers to dnsPromises.resolveTxt(), e.g. { entries: ['...'], type: 'TXT' }

Here is an example of the result object:

[ { type: 'A', address: '127.0.0.1', ttl: 299 },
  { type: 'CNAME', value: 'example.com' },
  { type: 'MX', exchange: 'alt4.aspmx.l.example.com', priority: 50 },
  { type: 'NS', value: 'ns1.example.com' },
  { type: 'TXT', entries: [ 'v=spf1 include:_spf.example.com ~all' ] },
  { type: 'SOA',
    nsname: 'ns1.example.com',
    hostmaster: 'admin.example.com',
    serial: 156696742,
    refresh: 900,
    retry: 900,
    expire: 1800,
    minttl: 60 } ];
js

dnsPromises.resolveCaa(hostname)#

Uses the DNS protocol to resolve CAA records for the hostname. On success, the Promise is resolved with an array of objects containing available certification authority authorization records available for the hostname (e.g. [{critical: 0, iodef: 'mailto:pki@example.com'},{critical: 128, issue: 'pki.example.com'}]).

dnsPromises.resolveCname(hostname)#

Uses the DNS protocol to resolve CNAME records for the hostname. On success, the Promise is resolved with an array of canonical name records available for the hostname (e.g. ['bar.example.com']).

dnsPromises.resolveMx(hostname)#

Uses the DNS protocol to resolve mail exchange records (MX records) for the hostname. On success, the Promise is resolved with an array of objects containing both a priority and exchange property (e.g. [{priority: 10, exchange: 'mx.example.com'}, ...]).

dnsPromises.resolveNaptr(hostname)#

Uses the DNS protocol to resolve regular expression-based records (NAPTR records) for the hostname. On success, the Promise is resolved with an array of objects with the following properties:

  • flags
  • service
  • regexp
  • replacement
  • order
  • preference
({
  flags: 's',
  service: 'SIP+D2U',
  regexp: '',
  replacement: '_sip._udp.example.com',
  order: 30,
  preference: 100,
});
js

dnsPromises.resolveNs(hostname)#

Uses the DNS protocol to resolve name server records (NS records) for the hostname. On success, the Promise is resolved with an array of name server records available for hostname (e.g. ['ns1.example.com', 'ns2.example.com']).

dnsPromises.resolvePtr(hostname)#

Uses the DNS protocol to resolve pointer records (PTR records) for the hostname. On success, the Promise is resolved with an array of strings containing the reply records.

dnsPromises.resolveSoa(hostname)#

Uses the DNS protocol to resolve a start of authority record (SOA record) for the hostname. On success, the Promise is resolved with an object with the following properties:

  • nsname
  • hostmaster
  • serial
  • refresh
  • retry
  • expire
  • minttl
({
  nsname: 'ns.example.com',
  hostmaster: 'root.example.com',
  serial: 2013101809,
  refresh: 10000,
  retry: 2400,
  expire: 604800,
  minttl: 3600,
});
js

dnsPromises.resolveSrv(hostname)#

Uses the DNS protocol to resolve service records (SRV records) for the hostname. On success, the Promise is resolved with an array of objects with the following properties:

  • priority
  • weight
  • port
  • name
({
  priority: 10,
  weight: 5,
  port: 21223,
  name: 'service.example.com',
});
js

dnsPromises.resolveTlsa(hostname)#

Uses the DNS protocol to resolve certificate associations (TLSA records) for the hostname. On success, the Promise is resolved with an array of objects with these properties:

  • certUsage
  • selector
  • match
  • data
({
  certUsage: 3,
  selector: 1,
  match: 1,
  data: [ArrayBuffer],
});
js

dnsPromises.resolveTxt(hostname)#

Uses the DNS protocol to resolve text queries (TXT records) for the hostname. On success, the Promise is resolved with a two-dimensional array of the text records available for hostname (e.g. [ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]). Each sub-array contains TXT chunks of one record. Depending on the use case, these could be either joined together or treated separately.

dnsPromises.reverse(ip)#

Performs a reverse DNS query that resolves an IPv4 or IPv6 address to an array of host names.

On error, the Promise is rejected with an Error object, where err.code is one of the DNS error codes.

dnsPromises.setDefaultResultOrder(order)#

  • order <string> must be 'ipv4first', 'ipv6first' or 'verbatim'.

Set the default value of order in dns.lookup() and dnsPromises.lookup(). The value could be:

  • ipv4first: sets default order to ipv4first.
  • ipv6first: sets default order to ipv6first.
  • verbatim: sets default order to verbatim.

The default is verbatim and dnsPromises.setDefaultResultOrder() have higher priority than --dns-result-order. When using worker threads, dnsPromises.setDefaultResultOrder() from the main thread won't affect the default dns orders in workers.

dnsPromises.getDefaultResultOrder()#

Get the value of dnsOrder.

dnsPromises.setServers(servers)#

Sets the IP address and port of servers to be used when performing DNS resolution. The servers argument is an array of RFC 5952 formatted addresses. If the port is the IANA default DNS port (53) it can be omitted.

dnsPromises.setServers([
  '8.8.8.8',
  '[2001:4860:4860::8888]',
  '8.8.8.8:1053',
  '[2001:4860:4860::8888]:1053',
]);
js

An error will be thrown if an invalid address is provided.

The dnsPromises.setServers() method must not be called while a DNS query is in progress.

This method works much like resolve.conf. That is, if attempting to resolve with the first server provided results in a NOTFOUND error, the resolve() method will not attempt to resolve with subsequent servers provided. Fallback DNS servers will only be used if the earlier ones time out or result in some other error.

Error codes#

Each DNS query can return one of the following error codes:

  • dns.NODATA: DNS server returned an answer with no data.
  • dns.FORMERR: DNS server claims query was misformatted.
  • dns.SERVFAIL: DNS server returned general failure.
  • dns.NOTFOUND: Domain name not found.
  • dns.NOTIMP: DNS server does not implement the requested operation.
  • dns.REFUSED: DNS server refused query.
  • dns.BADQUERY: Misformatted DNS query.
  • dns.BADNAME: Misformatted host name.
  • dns.BADFAMILY: Unsupported address family.
  • dns.BADRESP: Misformatted DNS reply.
  • dns.CONNREFUSED: Could not contact DNS servers.
  • dns.TIMEOUT: Timeout while contacting DNS servers.
  • dns.EOF: End of file.
  • dns.FILE: Error reading file.
  • dns.NOMEM: Out of memory.
  • dns.DESTRUCTION: Channel is being destroyed.
  • dns.BADSTR: Misformatted string.
  • dns.BADFLAGS: Illegal flags specified.
  • dns.NONAME: Given host name is not numeric.
  • dns.BADHINTS: Illegal hints flags specified.
  • dns.NOTINITIALIZED: c-ares library initialization not yet performed.
  • dns.LOADIPHLPAPI: Error loading iphlpapi.dll.
  • dns.ADDRGETNETWORKPARAMS: Could not find GetNetworkParams function.
  • dns.CANCELLED: DNS query cancelled.

The dnsPromises API also exports the above error codes, e.g., dnsPromises.NODATA.

Implementation considerations#

Although dns.lookup() and the various dns.resolve*()/dns.reverse() functions have the same goal of associating a network name with a network address (or vice versa), their behavior is quite different. These differences can have subtle but significant consequences on the behavior of Node.js programs.

dns.lookup()#

Under the hood, dns.lookup() uses the same operating system facilities as most other programs. For instance, dns.lookup() will almost always resolve a given name the same way as the ping command. On most POSIX-like operating systems, the behavior of the dns.lookup() function can be modified by changing settings in nsswitch.conf(5) and/or resolv.conf(5), but changing these files will change the behavior of all other programs running on the same operating system.

Though the call to dns.lookup() will be asynchronous from JavaScript's perspective, it is implemented as a synchronous call to getaddrinfo(3) that runs on libuv's threadpool. This can have surprising negative performance implications for some applications, see the UV_THREADPOOL_SIZE documentation for more information.

Various networking APIs will call dns.lookup() internally to resolve host names. If that is an issue, consider resolving the host name to an address using dns.resolve() and using the address instead of a host name. Also, some networking APIs (such as socket.connect() and dgram.createSocket()) allow the default resolver, dns.lookup(), to be replaced.

dns.resolve(), dns.resolve*(), and dns.reverse()#

These functions are implemented quite differently than dns.lookup(). They do not use getaddrinfo(3) and they always perform a DNS query on the network. This network communication is always done asynchronously and does not use libuv's threadpool.

As a result, these functions cannot have the same negative impact on other processing that happens on libuv's threadpool that dns.lookup() can have.

They do not use the same set of configuration files that dns.lookup() uses. For instance, they do not use the configuration from /etc/hosts.

Domain#

Stability: 0 - Deprecated

This module is pending deprecation. Once a replacement API has been finalized, this module will be fully deprecated. Most developers should not have cause to use this module. Users who absolutely must have the functionality that domains provide may rely on it for the time being but should expect to have to migrate to a different solution in the future.

Domains provide a way to handle multiple different IO operations as a single group. If any of the event emitters or callbacks registered to a domain emit an 'error' event, or throw an error, then the domain object will be notified, rather than losing the context of the error in the process.on('uncaughtException') handler, or causing the program to exit immediately with an error code.

Warning: Don't ignore errors!#

Domain error handlers are not a substitute for closing down a process when an error occurs.

By the very nature of how throw works in JavaScript, there is almost never any way to safely "pick up where it left off", without leaking references, or creating some other sort of undefined brittle state.

The safest way to respond to a thrown error is to shut down the process. Of course, in a normal web server, there may be many open connections, and it is not reasonable to abruptly shut those down because an error was triggered by someone else.

The better approach is to send an error response to the request that triggered the error, while letting the others finish in their normal time, and stop listening for new requests in that worker.

In this way, domain usage goes hand-in-hand with the cluster module, since the primary process can fork a new worker when a worker encounters an error. For Node.js programs that scale to multiple machines, the terminating proxy or service registry can take note of the failure, and react accordingly.

For example, this is not a good idea:

// XXX WARNING! BAD IDEA!

const d = require('node:domain').create();
d.on('error', (er) => {
  // The error won't crash the process, but what it does is worse!
  // Though we've prevented abrupt process restarting, we are leaking
  // a lot of resources if this ever happens.
  // This is no better than process.on('uncaughtException')!
  console.log(`error, but oh well ${er.message}`);
});
d.run(() => {
  require('node:http').createServer((req, res) => {
    handleRequest(req, res);
  }).listen(PORT);
});
js

By using the context of a domain, and the resilience of separating our program into multiple worker processes, we can react more appropriately, and handle errors with much greater safety.

// Much better!

const cluster = require('node:cluster');
const PORT = +process.env.PORT || 1337;

if (cluster.isPrimary) {
  // A more realistic scenario would have more than 2 workers,
  // and perhaps not put the primary and worker in the same file.
  //
  // It is also possible to get a bit fancier about logging, and
  // implement whatever custom logic is needed to prevent DoS
  // attacks and other bad behavior.
  //
  // See the options in the cluster documentation.
  //
  // The important thing is that the primary does very little,
  // increasing our resilience to unexpected errors.

  cluster.fork();
  cluster.fork();

  cluster.on('disconnect', (worker) => {
    console.error('disconnect!');
    cluster.fork();
  });

} else {
  // the worker
  //
  // This is where we put our bugs!

  const domain = require('node:domain');

  // See the cluster documentation for more details about using
  // worker processes to serve requests. How it works, caveats, etc.

  const server = require('node:http').createServer((req, res) => {
    const d = domain.create();
    d.on('error', (er) => {
      console.error(`error ${er.stack}`);

      // We're in dangerous territory!
      // By definition, something unexpected occurred,
      // which we probably didn't want.
      // Anything can happen now! Be very careful!

      try {
        // Make sure we close down within 30 seconds
        const killtimer = setTimeout(() => {
          process.exit(1);
        }, 30000);
        // But don't keep the process open just for that!
        killtimer.unref();

        // Stop taking new requests.
        server.close();

        // Let the primary know we're dead. This will trigger a
        // 'disconnect' in the cluster primary, and then it will fork
        // a new worker.
        cluster.worker.disconnect();

        // Try to send an error to the request that triggered the problem
        res.statusCode = 500;
        res.setHeader('content-type', 'text/plain');
        res.end('Oops, there was a problem!\n');
      } catch (er2) {
        // Oh well, not much we can do at this point.
        console.error(`Error sending 500! ${er2.stack}`);
      }
    });

    // Because req and res were created before this domain existed,
    // we need to explicitly add them.
    // See the explanation of implicit vs explicit binding below.
    d.add(req);
    d.add(res);

    // Now run the handler function in the domain.
    d.run(() => {
      handleRequest(req, res);
    });
  });
  server.listen(PORT);
}

// This part is not important. Just an example routing thing.
// Put fancy application logic here.
function handleRequest(req, res) {
  switch (req.url) {
    case '/error':
      // We do some async stuff, and then...
      setTimeout(() => {
        // Whoops!
        flerb.bark();
      }, timeout);
      break;
    default:
      res.end('ok');
  }
}
js

Additions to Error objects#

Any time an Error object is routed through a domain, a few extra fields are added to it.

  • error.domain The domain that first handled the error.
  • error.domainEmitter The event emitter that emitted an 'error' event with the error object.
  • error.domainBound The callback function which was bound to the domain, and passed an error as its first argument.
  • error.domainThrown A boolean indicating whether the error was thrown, emitted, or passed to a bound callback function.

Implicit binding#

If domains are in use, then all new EventEmitter objects (including Stream objects, requests, responses, etc.) will be implicitly bound to the active domain at the time of their creation.

Additionally, callbacks passed to low-level event loop requests (such as to fs.open(), or other callback-taking methods) will automatically be bound to the active domain. If they throw, then the domain will catch the error.

In order to prevent excessive memory usage, Domain objects themselves are not implicitly added as children of the active domain. If they were, then it would be too easy to prevent request and response objects from being properly garbage collected.

To nest Domain objects as children of a parent Domain they must be explicitly added.

Implicit binding routes thrown errors and 'error' events to the Domain's 'error' event, but does not register the EventEmitter on the Domain. Implicit binding only takes care of thrown errors and 'error' events.

Explicit binding#

Sometimes, the domain in use is not the one that ought to be used for a specific event emitter. Or, the event emitter could have been created in the context of one domain, but ought to instead be bound to some other domain.

For example, there could be one domain in use for an HTTP server, but perhaps we would like to have a separate domain to use for each request.

That is possible via explicit binding.

// Create a top-level domain for the server
const domain = require('node:domain');
const http = require('node:http');
const serverDomain = domain.create();

serverDomain.run(() => {
  // Server is created in the scope of serverDomain
  http.createServer((req, res) => {
    // Req and res are also created in the scope of serverDomain
    // however, we'd prefer to have a separate domain for each request.
    // create it first thing, and add req and res to it.
    const reqd = domain.create();
    reqd.add(req);
    reqd.add(res);
    reqd.on('error', (er) => {
      console.error('Error', er, req.url);
      try {
        res.writeHead(500);
        res.end('Error occurred, sorry.');
      } catch (er2) {
        console.error('Error sending 500', er2, req.url);
      }
    });
  }).listen(1337);
});
js

domain.create()#

Class: Domain#

The Domain class encapsulates the functionality of routing errors and uncaught exceptions to the active Domain object.

To handle the errors that it catches, listen to its 'error' event.

domain.members#

An array of event emitters that have been explicitly added to the domain.

domain.add(emitter)#

Explicitly adds an emitter to the domain. If any event handlers called by the emitter throw an error, or if the emitter emits an 'error' event, it will be routed to the domain's 'error' event, just like with implicit binding.

If the EventEmitter was already bound to a domain, it is removed from that one, and bound to this one instead.

domain.bind(callback)#

The returned function will be a wrapper around the supplied callback function. When the returned function is called, any errors that are thrown will be routed to the domain's 'error' event.

const d = domain.create();

function readSomeFile(filename, cb) {
  fs.readFile(filename, 'utf8', d.bind((er, data) => {
    // If this throws, it will also be passed to the domain.
    return cb(er, data ? JSON.parse(data) : null);
  }));
}

d.on('error', (er) => {
  // An error occurred somewhere. If we throw it now, it will crash the program
  // with the normal line number and stack message.
});
js

domain.enter()#

The enter() method is plumbing used by the run(), bind(), and intercept() methods to set the active domain. It sets domain.active and process.domain to the domain, and implicitly pushes the domain onto the domain stack managed by the domain module (see domain.exit() for details on the domain stack). The call to enter() delimits the beginning of a chain of asynchronous calls and I/O operations bound to a domain.

Calling enter() changes only the active domain, and does not alter the domain itself. enter() and exit() can be called an arbitrary number of times on a single domain.

domain.exit()#

The exit() method exits the current domain, popping it off the domain stack. Any time execution is going to switch to the context of a different chain of asynchronous calls, it's important to ensure that the current domain is exited. The call to exit() delimits either the end of or an interruption to the chain of asynchronous calls and I/O operations bound to a domain.

If there are multiple, nested domains bound to the current execution context, exit() will exit any domains nested within this domain.

Calling exit() changes only the active domain, and does not alter the domain itself. enter() and exit() can be called an arbitrary number of times on a single domain.

domain.intercept(callback)#

This method is almost identical to domain.bind(callback). However, in addition to catching thrown errors, it will also intercept Error objects sent as the first argument to the function.

In this way, the common if (err) return callback(err); pattern can be replaced with a single error handler in a single place.

const d = domain.create();

function readSomeFile(filename, cb) {
  fs.readFile(filename, 'utf8', d.intercept((data) => {
    // Note, the first argument is never passed to the
    // callback since it is assumed to be the 'Error' argument
    // and thus intercepted by the domain.

    // If this throws, it will also be passed to the domain
    // so the error-handling logic can be moved to the 'error'
    // event on the domain instead of being repeated throughout
    // the program.
    return cb(null, JSON.parse(data));
  }));
}

d.on('error', (er) => {
  // An error occurred somewhere. If we throw it now, it will crash the program
  // with the normal line number and stack message.
});
js

domain.remove(emitter)#

The opposite of domain.add(emitter). Removes domain handling from the specified emitter.

domain.run(fn[, ...args])#

Run the supplied function in the context of the domain, implicitly binding all event emitters, timers, and low-level requests that are created in that context. Optionally, arguments can be passed to the function.

This is the most basic way to use a domain.

const domain = require('node:domain');
const fs = require('node:fs');
const d = domain.create();
d.on('error', (er) => {
  console.error('Caught error!', er);
});
d.run(() => {
  process.nextTick(() => {
    setTimeout(() => { // Simulating some various async stuff
      fs.open('non-existent file', 'r', (er, fd) => {
        if (er) throw er;
        // proceed...
      });
    }, 100);
  });
});
js

In this example, the d.on('error') handler will be triggered, rather than crashing the program.

Domains and promises#

As of Node.js 8.0.0, the handlers of promises are run inside the domain in which the call to .then() or .catch() itself was made:

const d1 = domain.create();
const d2 = domain.create();

let p;
d1.run(() => {
  p = Promise.resolve(42);
});

d2.run(() => {
  p.then((v) => {
    // running in d2
  });
});
js

A callback may be bound to a specific domain using domain.bind(callback):

const d1 = domain.create();
const d2 = domain.create();

let p;
d1.run(() => {
  p = Promise.resolve(42);
});

d2.run(() => {
  p.then(p.domain.bind((v) => {
    // running in d1
  }));
});
js

Domains will not interfere with the error handling mechanisms for promises. In other words, no 'error' event will be emitted for unhandled Promise rejections.

DTLS#

Stability: 1.1 - Active Development

The node:dtls module provides an implementation of the Datagram Transport Layer Security (DTLS) protocol over UDP. DTLS provides TLS-equivalent security guarantees for datagram-based communication, including confidentiality, integrity, and authentication.

To use this module, it must be enabled at build time with the --experimental-dtls configure flag and at runtime with the --experimental-dtls CLI flag.

import { listen, connect } from 'node:dtls';
const { listen, connect } = require('node:dtls');
javascript

Permission model#

When using the Permission Model, the --allow-net flag must be passed to allow DTLS network operations. Without it, calling dtls.connect() or dtls.listen() will throw an ERR_ACCESS_DENIED error.

node --permission --allow-fs-read=* --experimental-dtls index.mjs
Error: Access to this API has been restricted. Use --allow-net to manage permissions.
  code: 'ERR_ACCESS_DENIED',
  permission: 'Net',
}
console

Creating a DTLSEndpoint instance without connecting or listening is permitted even without --allow-net, since no network I/O occurs until dtls.connect() or dtls.listen() is called.

DTLS vs TLS#

DTLS is designed for UDP transport and differs from TLS in several key ways:

  • No stream guarantees: Messages may arrive out of order or be lost. DTLS preserves datagram semantics.
  • One socket, many peers: A single UDP socket can serve multiple DTLS sessions. The DTLSEndpoint manages this multiplexing.
  • Cookie exchange: DTLS servers use a stateless cookie mechanism (HelloVerifyRequest) to prevent denial-of-service amplification attacks.
  • Retransmission: DTLS handles handshake retransmission internally since UDP does not guarantee delivery.

dtls.listen(callback, options)#

  • callback <Function> Called for each new DTLS session accepted by the server.
    • session {DTLSSession} The new session.
  • options <Object>
    • cert <string> | <Buffer> Server certificate in PEM format. Required.
    • key <string> | <Buffer> Server private key in PEM format. Required.
    • secureContext {DTLSSecureContext} A context from dtls.createSecureContext() to use instead of building one from the credential options below. Must have been created with isServer: true. Cannot be combined with any option the context already carries.
    • sni <Object> | <Function> Server Name Indication. A map of host names to the identity to serve them with, or a function returning one. Cannot be combined with secureContext; set it on the context instead. See Server Name Indication.
    • passphrase <string> Passphrase to decrypt key, if it is encrypted. Ignored when key is not encrypted. Unlike key and cert, this must be a string, matching tls.createSecureContext().
    • port <number> Port to bind to. Required.
    • host <string> Address to bind to. Default: '0.0.0.0'.
    • ca <string> | <Buffer> | <string>[] | <Buffer>[] CA certificates in PEM format.
    • ciphers <string> OpenSSL cipher list string.
    • alpn <string>[] | <Buffer> ALPN protocol names. Each name must be between 1 and 255 bytes. A Buffer must already be in ALPN wire format: one length byte followed by that many bytes, repeated.
    • srtp <string> Colon-separated SRTP protection profile names (e.g., 'SRTP_AES128_CM_SHA1_80:SRTP_AEAD_AES_128_GCM').
    • requestCert <boolean> Request a certificate from the client. Default: false.
    • rejectUnauthorized <boolean> Only has an effect together with requestCert. When true, a client that presents no certificate, or one that does not chain to a trusted CA, is rejected during the handshake and receives a TLS alert. When false, the certificate is still requested and verified but the handshake completes regardless, leaving the decision to the application via session.authorized. Default: true.
    • mtu <number> Maximum size in bytes of a DTLS datagram. Default: 1200.
    • handshakeTimeout <number> Milliseconds a handshake may take before it is abandoned. 0 disables it. Default: 60000. See Handshake timeout.
    • ipv6Only <boolean> When true, an IPv6 endpoint serves IPv6 only. When false, binding '::' also accepts IPv4 peers, which arrive with mapped addresses such as '::ffff:203.0.113.1' -- anything keyed on the peer address, including maxSessionsPerHost, sees them in that form. Has no effect on an IPv4 endpoint. Default: false.
    • reusePort <boolean> When true, sets SO_REUSEPORT, so several processes may bind the same port and the kernel spreads arriving datagrams between them. Every one of them must set it. Default: false.
    • udpReceiveBufferSize <number> Size in bytes for the socket's receive buffer (SO_RCVBUF). Raising it gives the endpoint room for bursts that the default would drop. The kernel clamps this to its own maximum. Default: the system default.
    • udpSendBufferSize <number> Size in bytes for the socket's send buffer (SO_SNDBUF). Clamped as above. Default: the system default.
    • udpTTL <number> IP time-to-live for outgoing datagrams, from 1 to 255. Default: the system default.
    • maxSessions <number> The maximum number of concurrent sessions the endpoint will hold. Set to 0 for no limit. Default: 10000.
    • maxSessionsPerHost <number> The maximum number of concurrent sessions from any single source IP address, ignoring port. Set to 0 for no limit. Default: 1000.
    • sessionIdContext <string> Opaque identifier scoping resumable sessions to this server, at most 32 bytes. Default: a value derived from process.argv, as in tls.createServer().
  • Returns: {DTLSEndpoint}

Creates a DTLS server bound to the specified address and port. The server uses automatic HMAC-based cookie exchange for DoS protection. See Denial of service.

Binding failures are thrown with the code the operating system gave, as in net and dgram: an address already in use throws an error whose code is 'EADDRINUSE', with errno and syscall set.

import { listen } from 'node:dtls';
import { readFileSync } from 'node:fs';

const endpoint = listen((session) => {
  session.onmessage = (data) => {
    console.log('Received:', data.toString());
    session.send('pong');
  };

  session.onhandshake = (protocol) => {
    console.log('Handshake complete:', protocol);
  };
}, {
  cert: readFileSync('server-cert.pem'),
  key: readFileSync('server-key.pem'),
  port: 4433,
});

console.log('DTLS server listening on', endpoint.address);
mjs

dtls.connect(host, port[, options])#

  • host <string> Remote host to connect to, as an IPv4 or IPv6 literal. Host names are not resolved.
  • port <number> Remote port to connect to.
  • options <Object>
    • ca <string> | <Buffer> | <string>[] | <Buffer>[] CA certificates in PEM format.
    • cert <string> | <Buffer> Client certificate in PEM format.
    • key <string> | <Buffer> Client private key in PEM format.
    • secureContext {DTLSSecureContext} A context from dtls.createSecureContext() to use instead of building one from the credential options below. Must not have been created with isServer: true. Cannot be combined with any option the context already carries.
    • psk <Object> | <Function> A pre-shared key as { identity, key }, or a function returning one. See Pre-shared keys.
    • session <Buffer> A session from session.session on an earlier connection, to resume rather than handshake in full. See Session resumption.
    • passphrase <string> Passphrase to decrypt key, if it is encrypted. Ignored when key is not encrypted. Unlike key and cert, this must be a string, matching tls.createSecureContext().
    • rejectUnauthorized <boolean> When true, the server's certificate must both chain to a trusted CA and match the expected identity (servername, or host when servername is not set); otherwise the handshake is aborted and session.opened rejects. When false, the certificate is still verified and the handshake completes regardless, leaving the decision to the application via session.authorized and session.authorizationError. Default: true.
    • servername <string> Server name used for the SNI (Server Name Indication) extension and as the identity checked during certificate verification. Default: the host argument. Set to '' to disable SNI. SNI is never sent for IP address literals.
    • bindHost <string> Local bind address. Default: '::' when host is an IPv6 literal, otherwise '0.0.0.0'. The local socket must be in the same address family as the peer.
    • bindPort <number> Local bind port. Default: 0 (ephemeral).
    • alpn <string>[] | <Buffer> ALPN protocol names. Each name must be between 1 and 255 bytes. A Buffer must already be in ALPN wire format: one length byte followed by that many bytes, repeated.
    • srtp <string> SRTP protection profile names.
    • mtu <number> Maximum size in bytes of a DTLS datagram. Default: 1200.
    • handshakeTimeout <number> Milliseconds a handshake may take before it is abandoned and session.opened rejects. 0 disables it. Default: 60000. See Handshake timeout.
  • Returns: {DTLSSession}

Connects to a DTLS server. Returns a DTLSSession whose opened property is a Promise that resolves when the handshake completes.

import { connect } from 'node:dtls';
import { readFileSync } from 'node:fs';

const session = connect('127.0.0.1', 4433, {
  ca: [readFileSync('ca-cert.pem')],
});

await session.opened;
session.send('hello');

session.onmessage = (data) => {
  console.log('Received:', data.toString());
};
mjs

dtls.createSecureContext([options])#

Options marked "Servers only" require isServer: true. Passing one to a client context throws ERR_INVALID_ARG_VALUE, rather than being ignored or applied where it can have no effect.

Creates a reusable secure context. Pass it to dtls.listen() or dtls.connect() as secureContext in place of the credential options.

A context holds a parsed certificate and key and, when ca is given, its own certificate store; roughly 28 KiB in total. Building one per connection is therefore expensive in memory rather than in time -- two thousand of them cost about 54 MiB, against 2 MiB when a single context is shared. Clients opening many connections should build the context once.

The peer identity checked during verification is not part of the context. It is bound to each connection from servername (or the host), so one context can be used against different peers and still reject the wrong certificate.

isServer is fixed when the context is created, because it selects the underlying OpenSSL method. Passing a server context to dtls.connect(), or a client context to dtls.listen(), throws.

import { connect, createSecureContext, listen } from 'node:dtls';
import { readFileSync } from 'node:fs';

const serverContext = createSecureContext({
  cert: readFileSync('server-cert.pem'),
  key: readFileSync('server-key.pem'),
  isServer: true,
});

// One context, several endpoints.
const a = listen(onsession, { secureContext: serverContext, port: 5684 });
const b = listen(onsession, { secureContext: serverContext, port: 5685 });

const clientContext = createSecureContext({
  ca: readFileSync('ca-cert.pem'),
});

// One context, many connections, each verified against its own name.
const s1 = connect('192.0.2.1', 5684, {
  secureContext: clientContext,
  servername: 'a.example.com',
});
const s2 = connect('192.0.2.2', 5684, {
  secureContext: clientContext,
  servername: 'b.example.com',
});
mjs

Server Name Indication#

An endpoint can serve more than one identity by giving listen() an sni map, or a function. Each key of a map is a host name and each value is either a DTLSSecureContext created with isServer: true, or a plain object of the same options dtls.createSecureContext() takes:

import { createSecureContext, listen } from 'node:dtls';
import { readFileSync } from 'node:fs';

const endpoint = listen(onsession, {
  cert: readFileSync('default-cert.pem'),
  key: readFileSync('default-key.pem'),
  port: 5684,
  sni: {
    'api.example.com': {
      cert: readFileSync('api-cert.pem'),
      key: readFileSync('api-key.pem'),
    },
    'www.example.com': createSecureContext({
      cert: readFileSync('www-cert.pem'),
      key: readFileSync('www-key.pem'),
      isServer: true,
    }),
    '*': {
      cert: readFileSync('default-cert.pem'),
      key: readFileSync('default-key.pem'),
    },
  },
});
mjs

The '*' key is the fallback, used when the client's name matches nothing and when the client sends no name at all. Without it, an unmatched name is refused with an unrecognized_name alert rather than falling back to the endpoint's own cert and key; providing an sni map is taken to mean that only the names in it are served. tls.createServer() differs here: its SNICallback falls back to the default identity silently.

Verification follows the selected identity, so an entry carrying its own ca accepts only client certificates issued under it. requestCert and rejectUnauthorized are not per-identity: they belong to the endpoint and apply to every name it serves.

A function may be given instead of a map, for identities that are chosen rather than enumerated:

listen(onsession, {
  port: 5684,
  cert,
  key,
  sni: (servername) => contexts.get(servername),
});
mjs

It is called with the name the client asked for, or undefined if the client sent no SNI extension, and returns what a map entry holds: a dtls.createSecureContext() result or the options to build one. Returning nothing declines the name, which is refused exactly as an unmatched map with no '*' entry is, rather than falling back to the endpoint's own certificate.

The function runs during the handshake and must return synchronously, so it cannot consult a database. Returning a prepared context is worth doing: building one from options parses the certificate again on every handshake.

An exception thrown by the function fails that handshake and is reported to the session's error handler, like any other handshake failure. It does not reach the process as an uncaught exception.

The certificate and the cipher list both follow the selected context. Pre-shared keys do not. OpenSSL installs the PSK callbacks on the connection when it is created, before any name is known, and selecting an identity does not replace them, so the keys a server accepts are always the endpoint's own. A psk given on an SNI identity is never consulted, and an identity cannot be served over PSK alone.

sni belongs to the secure context rather than to the endpoint, so it can be given to dtls.createSecureContext() and cannot be combined with a secureContext that already exists. Applying it to a prepared context would reconfigure that context for every endpoint sharing it, and the identities a server serves are part of what its context is.

A connection refused for an unrecognized name still reaches the listen() callback: the session exists once the client's address is validated, which happens before the name is examined. It then fails like any other handshake failure.

Denial of service#

Cookie exchange proves a peer can receive at its claimed address, but it does not limit how many sessions that peer may then establish, and each session holds a TLS state machine, two buffers and a timer. maxSessions bounds the total; maxSessionsPerHost is what prevents one peer from taking all of it. A peer refused by either cap is answered with silence rather than an alert, because replying to an address that has not completed cookie exchange would create an amplification vector; a legitimate client retransmits and is admitted once there is room. Refusals are counted by endpointStats.serverRefusedCount.

Deployments serving many clients behind a single NAT may need to raise maxSessionsPerHost.

Handshake timeout#

A handshake that never finishes is abandoned after handshakeTimeout milliseconds, and its session error is DTLS handshake timeout.

OpenSSL already gives up on its own, but only after twelve retransmits on a doubling backoff capped at 60 seconds -- around eight minutes in total. Until then the session holds its place against maxSessions (see Denial of service), so handshakes that are started and abandoned can occupy an endpoint for the cost of starting them. That needs no spoofing: the peer completes the cookie exchange and then simply stops.

The two limits coexist and whichever comes first ends the handshake. The retransmit schedule itself is untouched, deliberately -- compressing it to force earlier failure would cause spurious retransmissions on exactly the lossy links DTLS is meant for.

The timeout covers resumed and PSK handshakes as well, and stops applying once the handshake completes; it is not an idle timeout.

A handshake can stall without either peer being at fault or aware. DTLS discards records it cannot authenticate rather than answering them (RFC 6347 section 4.1.2.1), so a mismatched pre-shared key or a cipher list with nothing in common produces silence rather than an alert. This timeout is what ends those.

Pre-shared keys#

DTLS can authenticate with a key both peers already hold instead of a certificate (RFC 4279). This is how it is usually deployed to constrained devices, which frequently have no certificate at all.

A server gives the identities it accepts; a client gives the one it is. No certificate is needed on either side:

import { connect, listen } from 'node:dtls';

const endpoint = listen(onsession, {
  port: 5684,
  psk: { 'device-42': deviceKey },
});

const client = connect('192.0.2.1', 5684, {
  psk: { identity: 'device-42', key: deviceKey },
});
mjs

Either side may pass a function instead, for keys that are looked up or derived rather than known up front. A server's is called with the identity the client offered and returns the key, or nothing to refuse it. A client's is called with the server's identity hint, if it sent one, and returns { identity, key }:

listen(onsession, {
  port: 5684,
  psk: (identity) => deriveKey(masterSecret, identity),
});
mjs

The callback runs during the handshake and must return synchronously, so it cannot consult a database. Where both are given, the map is checked first and the callback is only reached when the map has no answer -- a configuration using only the map never runs JavaScript inside the handshake.

An exception thrown by the callback fails that handshake and is reported to the session's error handler. It does not reach the process as an uncaught exception.

Cipher suites#

The default cipher list excludes PSK, so giving psk without ciphers enables the PSK suites. Supplying ciphers disables that and uses exactly what was asked for.

A server keeps the certificate suites as well, since it may serve both kinds of client on one port. A client does not: a client that configured a pre-shared key and no CA wants the key, and leaving the certificate suites enabled would let a server choose one, failing the handshake while verifying a certificate the caller never meant to rely on.

Forward-secret PSK key exchanges are preferred over plain PSK of the same strength. Plain PSK derives its keys from the shared secret alone, so anyone who later learns that key can decrypt traffic they recorded earlier. RSA-PSK is excluded: it needs a certificate and adds no forward secrecy.

CoAP requires TLS_PSK_WITH_AES_128_CCM_8 (RFC 7252), whose 64-bit authentication tag OpenSSL rejects at security level 1 and above. Node.js default is above it, so that suite has to be asked for explicitly and with the security level lowered:

listen(onsession, { port: 5684, psk, ciphers: 'PSK-AES128-CCM8@SECLEVEL=0' });
mjs

Failure modes#

A wrong key does not produce an error. The identity only names the key, so the handshake proceeds and the two sides derive different secrets; the first record that fails authentication is then discarded rather than answered, since DTLS discards invalid records instead of replying to them (RFC 6347 section 4.1.2.1). Neither peer is told anything and both retransmit.

A cipher list with nothing in common behaves the same way, which is what makes the CCM8 case above present as a stall rather than a rejection. Both are ended by handshakeTimeout, after 60 seconds by default.

An identity the server does not recognise is refused outright, and the client sees the handshake fail.

Session resumption#

A resumed handshake skips the server's certificate, which matters more here than it does over TCP: the Certificate flight is fragmented across several datagrams, and losing any one of them costs a retransmission timeout. Measured on loopback, a full handshake has the server send 1850 bytes in 4 packets against 280 bytes in 3 for a resumed one.

A client reads session.session once the session is open and passes it to a later dtls.connect():

import { connect } from 'node:dtls';

const first = connect('192.0.2.1', 5684, { ca, servername: 'device.example' });
await first.opened;
const ticket = first.session;        // Buffer.
await first.close();

const second = connect('192.0.2.1', 5684, {
  ca,
  servername: 'device.example',
  session: ticket,
});
await second.opened;
console.log(second.reused);          // True.
mjs

A session that the server will not accept -- expired, or issued by a different endpoint -- is not an error. The handshake simply proceeds in full, and session.reused is false.

The cookie exchange still happens for a resumed handshake, so resumption is not a way around the address validation described under Denial of service.

Binding to the authenticated host#

A session may only be resumed against the identity it was authenticated for -- the servername, or the host when there is none. Reusing it for anything else throws.

This is not a convenience check. A resumed handshake does not re-send or re-verify the peer's certificate; it inherits the authenticated identity of the original session. Replaying a session against a different host would therefore skip verification while appearing to succeed. For the same reason a session that did not come from session.session is rejected outright: nothing records which identity it belongs to, so it cannot be checked.

Resuming under rejectUnauthorized#

A session carries the verification result it was established with, so a session established with rejectUnauthorized: false cannot be resumed by a connection that asked for a verified peer. The handshake fails:

import { connect } from 'node:dtls';

// Connected without verifying anything.
const first = connect('192.0.2.1', 5684, { rejectUnauthorized: false });
await first.opened;
console.log(first.authorized);         // False.
const ticket = first.session;
await first.close();

const second = connect('192.0.2.1', 5684, {
  rejectUnauthorized: true,
  session: ticket,
});
await second.opened;                   // Rejects: verification failed.
mjs

The host is the same in both, so binding the session to its authenticated identity does not cover this on its own; what differs is whether the caller asked for the peer to be verified. Because a resumed handshake runs no verification of its own, the recorded result is re-checked once it completes, and a session whose peer never verified is refused wherever verification is required. session.authorized and session.authorizationError report the recorded result on a resumed session either way.

Ticket keys#

The key that encrypts session tickets is generated at random for each context, so by default a ticket is only good for the endpoint that issued it and only until the process restarts. Give every endpoint the same ticketKeys to let tickets be resumed across a restart or a cluster:

import { listen } from 'node:dtls';
import { randomBytes } from 'node:crypto';

const ticketKeys = randomBytes(80);    // Share this between processes.
const endpoint = listen(onsession, { cert, key, port: 5684, ticketKeys });
mjs

The length is OpenSSL's: a key name followed by an HMAC key and an AES key. It differs from the 48 bytes tls.createServer() uses, which is a layout node:tls defines for itself. Supplying the wrong length throws and reports the length expected.

Ticket keys are long-lived secrets. Anyone holding them can decrypt tickets and recover the sessions they protect, so treat them as key material and rotate them.

Class: DTLSSecureContext#

An opaque, reusable bundle of credentials and TLS settings, created by dtls.createSecureContext(). It cannot be constructed directly.

secureContext.isServer#

  • Returns: <boolean> true if the context was created for a server.

Class: DTLSEndpoint#

Manages a UDP socket and multiplexes DTLS sessions.

endpoint.address#

  • Returns: <Object> { address, family, port }

The local address the endpoint is bound to.

endpoint.stats#

The statistics collected for this endpoint. Read only. The stats object is live and updated as data flows through the endpoint.

endpoint.busy#

When true, the endpoint rejects new incoming connections. Can be set to implement backpressure.

endpoint.close()#

  • Returns: <Promise> Resolves when the endpoint is fully closed.

Gracefully closes the endpoint. All active sessions are closed with close_notify alerts before the UDP socket is released.

endpoint.destroy([error])#

Immediately destroys the endpoint without sending close_notify alerts.

endpoint.destroyed#

  • <boolean> True once the endpoint has been destroyed.

endpoint.closed#

  • <Promise> Resolves when the endpoint has fully closed.

endpoint[Symbol.asyncDispose]()#

Equivalent to calling endpoint.close().

Class: DTLSEndpoint.Stats#

A view of the collected statistics for an endpoint.

endpointStats.createdAt#

  • Type: <bigint> A timestamp indicating when the endpoint was created. Read only.

endpointStats.destroyedAt#

  • Type: <bigint> A timestamp indicating when the endpoint was destroyed. Read only.

endpointStats.bytesReceived#

  • Type: <bigint> The total number of bytes received by this endpoint. Read only.

endpointStats.bytesSent#

  • Type: <bigint> The total number of bytes sent by this endpoint. Read only.

endpointStats.packetsReceived#

  • Type: <bigint> The total number of UDP packets received by this endpoint. Read only.

endpointStats.packetsSent#

  • Type: <bigint> The total number of UDP packets sent by this endpoint. Read only.

endpointStats.serverSessions#

  • Type: <bigint> The total number of peer-initiated sessions accepted by this endpoint. Read only.

endpointStats.clientSessions#

  • Type: <bigint> The total number of sessions initiated by this endpoint. Read only.

endpointStats.serverBusyCount#

  • Type: <bigint> The total number of incoming connections rejected because the endpoint was marked busy. Read only.

endpointStats.serverRejectedCount#

  • Type: <bigint> The number of datagrams discarded before a handshake was attempted because they could not be a ClientHello. Read only.

Datagrams arriving at a listening endpoint that do not match an existing session are screened for the shape of a DTLS ClientHello record before any state is allocated for them. A steadily rising value indicates junk or scan traffic rather than failing clients, which are counted as sessions that never complete.

endpointStats.serverRefusedCount#

  • Type: <bigint> The number of otherwise valid handshake attempts refused because the endpoint was at maxSessions or the peer was at maxSessionsPerHost. Read only.

endpointStats.isConnected#

true if the stats object is still connected to the underlying endpoint. Once the endpoint is destroyed, the stats become a stale snapshot.

Class: DTLSSession#

Represents a DTLS association with a single remote peer.

session.send(data)#

  • data <string> | <Buffer> | <TypedArray> | <DataView> The data to send. At most 16384 bytes. A view sends the bytes it covers, so an offset or a subarray is sent as given rather than as the whole buffer behind it.
  • Returns: <number> The number of bytes written to the DTLS layer.

Send application data to the peer. The data is encrypted by DTLS before being sent over UDP. Can only be called after the handshake completes (session.opened has resolved).

DTLS carries application data in a single record per datagram and does not fragment it, so data must fit in one record. Sending more throws ERR_OUT_OF_RANGE. This limit is independent of the mtu option: a record larger than the path MTU is still sent, and is fragmented by IP.

Throws ERR_INVALID_STATE if the handshake has not completed, or if the session is closed or destroyed.

A successful return means the data was handed to the DTLS layer and written to the socket, not that the peer received it. DTLS runs over UDP, so application data may still be lost in transit.

session.close()#

  • Returns: <Promise> Resolves when the session is closed.

Initiates a graceful DTLS shutdown by sending a close_notify alert.

session.destroy([error])#

Immediately destroys the session without sending close_notify.

session.destroyed#

session.endpoint#

  • {DTLSEndpoint} The endpoint carrying this session. For a session from dtls.listen() this is the listening endpoint, shared with every other session on it; for one from dtls.connect() it is the endpoint created to carry that session alone.

session.servername#

  • <string> | <undefined> The server name for this session: the name the client sent in the SNI extension, read on either side of the connection. undefined when no name was sent. See Server name indication.

session.opened#

  • <Promise> Resolves with { protocol } when the DTLS handshake completes.

Rejects if the handshake fails, and also if the session is closed or destroyed before the handshake completes -- in that case with ERR_INVALID_STATE, or with the error passed to session.destroy() if one was given. The promise always settles, so awaiting it cannot hang.

session.closed#

  • <Promise> Settles when the session is fully closed. Resolves when the close was graceful, and rejects with the error when the session was destroyed with one, or when its endpoint was. The promise always settles, so awaiting it cannot hang.

session.remoteAddress#

  • Returns: <Object> { address, family, port }

session.protocol#

  • Returns: <string> The negotiated DTLS protocol version (e.g., 'DTLSv1.2').

session.cipher#

  • Returns: <Object> { name, standardName, version }

session.peerCertificate#

  • Returns: <string> | <undefined> The peer's certificate in PEM format, or undefined if the peer sent none.

This is the leaf certificate as PEM text and nothing else. For the issuer chain and the parsed fields, use session.peerX509Certificate, whose toString() returns this same PEM. Use session.authorized and session.authorizationError for the verification result rather than parsing either.

session.peerX509Certificate#

An X509Certificate for the peer's leaf certificate. The issuer chain is reachable through its issuerCertificate property, and the parsed fields -- subject, issuer, validFrom, validTo, fingerprint256, serialNumber and the rest -- are properties of that object.

Where tls.TLSSocket.getPeerCertificate() returns a plain dictionary with valid_from, valid_to and a chain walked through issuerCertificate, this returns the same X509Certificate class that tls.TLSSocket.getPeerX509Certificate() does. Call toLegacyObject() on it to get the dictionary form.

The same object is returned on every access once the peer's certificate is available.

session.session#

  • Returns: <Buffer> | <undefined> An opaque session for resuming this connection later, or undefined on a server session or before the handshake completes.

Pass it as the session option to a later dtls.connect(). It is bound to the host this connection authenticated against and is refused elsewhere; see Session resumption.

Server sessions return undefined: a server has no identity to bind the value to, and it is the client that carries a session between connections.

session.reused#

  • Returns: <boolean> true if this connection resumed an earlier session rather than performing a full handshake.

Like session.authorized, this reads false once the session is closed.

session.authorized#

  • Returns: <boolean> true if the peer presented a certificate chain that verified against the configured certificate authorities, and, for a client, matched the requested identity. false before the handshake completes.

session.authorizationError#

  • Returns: <string> | <undefined> The short X509 verification error code, for example 'CERT_HAS_EXPIRED' or 'HOSTNAME_MISMATCH', or undefined if the peer's chain verified.

A peer that presented no certificate at all reports 'UNABLE_TO_GET_ISSUER_CERT', so this can be used to distinguish "no certificate" from "a certificate that failed to verify".

The chain is verified even when rejectUnauthorized is false; the result is simply not enforced. That makes these two properties the way to apply a custom authorization policy:

import { connect } from 'node:dtls';

const session = connect('192.0.2.1', 4433, {
  ca: [caCert],
  servername: 'example.com',
  rejectUnauthorized: false,
});

await session.opened;

if (!session.authorized && session.authorizationError !== 'CERT_HAS_EXPIRED') {
  await session.close();
}
mjs

session.alpnProtocol#

  • Returns: <string> | <undefined> The negotiated ALPN protocol, or undefined if ALPN was not used.

If a server has alpn configured and a client offers only protocols the server does not support, the server sends a fatal no_application_protocol alert and the handshake fails, as required by RFC 7301 section 3.2. A server with no alpn configured declines the extension instead, and the handshake completes with no protocol negotiated.

session.srtpProfile#

session.stats#

The statistics collected for this session. Read only. The stats object is live and updated as data flows through the session.

session.exportKeyingMaterial(length, label[, context])#

  • length <number> Number of bytes to export. Must be an integer between 1 and 65536.
  • label <string> The label for the exported keying material.
  • context <Buffer> Optional context value.
  • Returns: <Buffer>

Exports keying material from the DTLS session, as defined in RFC 5705. This is commonly used with DTLS-SRTP to derive encryption keys for media streams.

Throws ERR_OUT_OF_RANGE if length is outside the accepted range. The upper bound is not imposed by RFC 5705; it exists so that a caller cannot request an arbitrarily large allocation, and is far above what any defined exporter needs (DTLS-SRTP uses 60 bytes).

Callback properties#

session.onmessage#

Set to receive application data from the peer.

session.onerror#

Set to receive error notifications.

session.onhandshake#

Set to receive handshake completion notifications.

session.onkeylog#

Set to receive TLS key log lines (for debugging with Wireshark).

session[Symbol.asyncDispose]()#

Equivalent to calling session.close().

Class: DTLSSession.Stats#

A view of the collected statistics for a session.

sessionStats.createdAt#

  • Type: <bigint> A timestamp indicating when the session was created. Read only.

sessionStats.destroyedAt#

  • Type: <bigint> A timestamp indicating when the session was destroyed. Read only.

sessionStats.closingAt#

  • Type: <bigint> A timestamp indicating when close() was called. Read only.

sessionStats.handshakeCompletedAt#

  • Type: <bigint> A timestamp indicating when the DTLS handshake completed. Read only.

sessionStats.bytesReceived#

  • Type: <bigint> The total number of application data bytes received. Read only.

sessionStats.bytesSent#

  • Type: <bigint> The total number of application data bytes sent. Read only.

sessionStats.messagesReceived#

  • Type: <bigint> The total number of application messages received. Read only.

sessionStats.messagesSent#

  • Type: <bigint> The total number of application messages sent. Read only.

sessionStats.retransmitCount#

  • Type: <bigint> The total number of DTLS handshake retransmissions. Read only.

sessionStats.isConnected#

true if the stats object is still connected to the underlying session. Once the session is destroyed, the stats become a stale snapshot.

DTLS-SRTP example#

DTLS-SRTP is used by WebRTC for media encryption. The DTLS handshake negotiates the SRTP protection profile and provides keying material.

import { listen, connect } from 'node:dtls';
import { readFileSync } from 'node:fs';

// Server with SRTP
const server = listen((session) => {
  session.onhandshake = () => {
    console.log('SRTP profile:', session.srtpProfile);
    const keys = session.exportKeyingMaterial(
      60,
      'EXTRACTOR-dtls_srtp',
    );
    console.log('SRTP keying material:', keys);
  };
}, {
  cert: readFileSync('server-cert.pem'),
  key: readFileSync('server-key.pem'),
  port: 5004,
  srtp: 'SRTP_AES128_CM_SHA1_80:SRTP_AEAD_AES_128_GCM',
});

// Client with SRTP
const session = connect('127.0.0.1', 5004, {
  rejectUnauthorized: false,
  srtp: 'SRTP_AEAD_AES_128_GCM:SRTP_AES128_CM_SHA1_80',
});

await session.opened;
console.log('Negotiated SRTP:', session.srtpProfile);
const keys = session.exportKeyingMaterial(60, 'EXTRACTOR-dtls_srtp');
mjs

MTU considerations#

Since libuv does not currently support path MTU discovery, the DTLS module uses a conservative default MTU of 1200 bytes. This value works across virtually all network paths but may be suboptimal for local networks.

This bounds the UDP payload, not the application payload: a record carries somewhat less once its header and MAC are accounted for. It is fixed when the endpoint is created and cannot be changed afterwards. It does not bound session.send(), which is limited by the DTLS record size instead.

The MTU can be configured via the mtu option:

// For a local network where you know the path MTU
const endpoint = listen(callback, {
  // ...
  mtu: 1400,
});
mjs

The minimum allowed MTU is 256 bytes. The maximum is 65535.

Inspector#

Stability: 2 - Stable

The node:inspector module provides an API for interacting with the V8 inspector.

It can be accessed using:

import * as inspector from 'node:inspector/promises';
const inspector = require('node:inspector/promises');
javascript

or

import * as inspector from 'node:inspector';
const inspector = require('node:inspector');
javascript

Promises API#

Stability: 1 - Experimental

Class: inspector.Session#

The inspector.Session is used for dispatching messages to the V8 inspector back-end and receiving message responses and notifications.

new inspector.Session()#

Create a new instance of the inspector.Session class. The inspector session needs to be connected through session.connect() before the messages can be dispatched to the inspector backend.

When using Session, the object outputted by the console API will not be released, unless we performed manually Runtime.DiscardConsoleEntries command.

Event: 'inspectorNotification'#
  • Type: <Object> The notification message object

Emitted when any notification from the V8 Inspector is received.

session.on('inspectorNotification', (message) => console.log(message.method));
// Debugger.paused
// Debugger.resumed
js

Caveat Breakpoints with same-thread session is not recommended, see support of breakpoints.

It is also possible to subscribe only to notifications with specific method:

Event: <inspector-protocol-method>#
  • Type: <Object> The notification message object

Emitted when an inspector notification is received that has its method field set to the <inspector-protocol-method> value.

The following snippet installs a listener on the 'Debugger.paused' event, and prints the reason for program suspension whenever program execution is suspended (through breakpoints, for example):

session.on('Debugger.paused', ({ params }) => {
  console.log(params.hitBreakpoints);
});
// [ '/the/file/that/has/the/breakpoint.js:11:0' ]
js

Caveat Breakpoints with same-thread session is not recommended, see support of breakpoints.

session.connect()#

Connects a session to the inspector back-end.

session.connectToMainThread()#

Connects a session to the main thread inspector back-end. An exception will be thrown if this API was not called on a Worker thread, or if --process-timeout is used, as the session could pause the main thread.

session.disconnect()#

Immediately close the session. All pending message callbacks will be called with an error. session.connect() will need to be called to be able to send messages again. Reconnected session will lose all inspector state, such as enabled agents or configured breakpoints.

session.post(method[, params])#

Posts a message to the inspector back-end.

import { Session } from 'node:inspector/promises';
try {
  const session = new Session();
  session.connect();
  const result = await session.post('Runtime.evaluate', { expression: '2 + 2' });
  console.log(result);
} catch (error) {
  console.error(error);
}
// Output: { result: { type: 'number', value: 4, description: '4' } }
mjs

The latest version of the V8 inspector protocol is published on the Chrome DevTools Protocol Viewer.

Node.js inspector supports all the Chrome DevTools Protocol domains declared by V8. Chrome DevTools Protocol domain provides an interface for interacting with one of the runtime agents used to inspect the application state and listen to the run-time events.

Example usage#

Apart from the debugger, various V8 Profilers are available through the DevTools protocol.

CPU profiler#

Here's an example showing how to use the CPU Profiler:

import { Session } from 'node:inspector/promises';
import fs from 'node:fs';
const session = new Session();
session.connect();

await session.post('Profiler.enable');
await session.post('Profiler.start');
// Invoke business logic under measurement here...

// some time later...
const { profile } = await session.post('Profiler.stop');

// Write profile to disk, upload, etc.
fs.writeFileSync('./profile.cpuprofile', JSON.stringify(profile));
mjs
Heap profiler#

Here's an example showing how to use the Heap Profiler:

import { Session } from 'node:inspector/promises';
import fs from 'node:fs';
const session = new Session();

const fd = fs.openSync('profile.heapsnapshot', 'w');

session.connect();

session.on('HeapProfiler.addHeapSnapshotChunk', (m) => {
  fs.writeSync(fd, m.params.chunk);
});

const result = await session.post('HeapProfiler.takeHeapSnapshot', null);
console.log('HeapProfiler.takeHeapSnapshot done:', result);
session.disconnect();
fs.closeSync(fd);
mjs

Callback API#

Class: inspector.Session#

The inspector.Session is used for dispatching messages to the V8 inspector back-end and receiving message responses and notifications.

new inspector.Session()#

Create a new instance of the inspector.Session class. The inspector session needs to be connected through session.connect() before the messages can be dispatched to the inspector backend.

When using Session, the object outputted by the console API will not be released, unless we performed manually Runtime.DiscardConsoleEntries command.

Event: 'inspectorNotification'#
  • Type: <Object> The notification message object

Emitted when any notification from the V8 Inspector is received.

session.on('inspectorNotification', (message) => console.log(message.method));
// Debugger.paused
// Debugger.resumed
js

Caveat Breakpoints with same-thread session is not recommended, see support of breakpoints.

It is also possible to subscribe only to notifications with specific method:

Event: <inspector-protocol-method>;#
  • Type: <Object> The notification message object

Emitted when an inspector notification is received that has its method field set to the <inspector-protocol-method> value.

The following snippet installs a listener on the 'Debugger.paused' event, and prints the reason for program suspension whenever program execution is suspended (through breakpoints, for example):

session.on('Debugger.paused', ({ params }) => {
  console.log(params.hitBreakpoints);
});
// [ '/the/file/that/has/the/breakpoint.js:11:0' ]
js

Caveat Breakpoints with same-thread session is not recommended, see support of breakpoints.

session.connect()#

Connects a session to the inspector back-end.

session.connectToMainThread()#

Connects a session to the main thread inspector back-end. An exception will be thrown if this API was not called on a Worker thread, or if --process-timeout is used, as the session could pause the main thread.

session.disconnect()#

Immediately close the session. All pending message callbacks will be called with an error. session.connect() will need to be called to be able to send messages again. Reconnected session will lose all inspector state, such as enabled agents or configured breakpoints.

session.post(method[, params][, callback])#

Posts a message to the inspector back-end. callback will be notified when a response is received. callback is a function that accepts two optional arguments: error and message-specific result.

session.post('Runtime.evaluate', { expression: '2 + 2' },
             (error, { result }) => console.log(result));
// Output: { type: 'number', value: 4, description: '4' }
js

The latest version of the V8 inspector protocol is published on the Chrome DevTools Protocol Viewer.

Node.js inspector supports all the Chrome DevTools Protocol domains declared by V8. Chrome DevTools Protocol domain provides an interface for interacting with one of the runtime agents used to inspect the application state and listen to the run-time events.

You can not set reportProgress to true when sending a HeapProfiler.takeHeapSnapshot or HeapProfiler.stopTrackingHeapObjects command to V8.

Example usage#

Apart from the debugger, various V8 Profilers are available through the DevTools protocol.

CPU profiler#

Here's an example showing how to use the CPU Profiler:

const inspector = require('node:inspector');
const fs = require('node:fs');
const session = new inspector.Session();
session.connect();

session.post('Profiler.enable', () => {
  session.post('Profiler.start', () => {
    // Invoke business logic under measurement here...

    // some time later...
    session.post('Profiler.stop', (err, { profile }) => {
      // Write profile to disk, upload, etc.
      if (!err) {
        fs.writeFileSync('./profile.cpuprofile', JSON.stringify(profile));
      }
    });
  });
});
js
Heap profiler#

Here's an example showing how to use the Heap Profiler:

const inspector = require('node:inspector');
const fs = require('node:fs');
const session = new inspector.Session();

const fd = fs.openSync('profile.heapsnapshot', 'w');

session.connect();

session.on('HeapProfiler.addHeapSnapshotChunk', (m) => {
  fs.writeSync(fd, m.params.chunk);
});

session.post('HeapProfiler.takeHeapSnapshot', null, (err, r) => {
  console.log('HeapProfiler.takeHeapSnapshot done:', err, r);
  session.disconnect();
  fs.closeSync(fd);
});
js

Common Objects#

inspector.close()#

Deactivates the inspector. If there are active connections, they are forcibly terminated. Blocks until the inspector server has fully stopped.

inspector.console#

  • Type: <Object> An object to send messages to the remote inspector console.
require('node:inspector').console.log('a message');
js

The inspector console does not have API parity with Node.js console.

inspector.open([port[, host[, wait]]])#

  • port <number> Port to listen on for inspector connections. Optional. Default: what was specified on the CLI.
  • host <string> Host to listen on for inspector connections. Optional. Default: what was specified on the CLI.
  • wait <boolean> Block until a client has connected. Optional. Default: false.
  • Returns: <Disposable> A Disposable that calls inspector.close().

Activate inspector on host and port. Equivalent to node --inspect=[[host:]port], but can be done programmatically after node has started.

If wait is true, will block until a client has connected to the inspect port and flow control has been passed to the debugger client.

See the security warning regarding the host parameter usage.

Throws an ERR_INSPECTOR_NOT_AVAILABLE error if --process-timeout is used.

inspector.url()#

Return the URL of the active inspector, or undefined if there is none.

$ node --inspect -p 'inspector.url()'
Debugger listening on ws://127.0.0.1:9229/166e272e-7a30-4d09-97ce-f1c012b43c34
For help, see: https://nodejs.org/learn/getting-started/debugging
ws://127.0.0.1:9229/166e272e-7a30-4d09-97ce-f1c012b43c34

$ node --inspect=localhost:3000 -p 'inspector.url()'
Debugger listening on ws://localhost:3000/51cf8d0e-3c36-4c59-8efd-54519839e56a
For help, see: https://nodejs.org/learn/getting-started/debugging
ws://localhost:3000/51cf8d0e-3c36-4c59-8efd-54519839e56a

$ node -p 'inspector.url()'
undefined
console

inspector.waitForDebugger()#

Blocks until a client (existing or connected later) has sent Runtime.runIfWaitingForDebugger command.

An exception will be thrown if there is no active inspector.

Integration with DevTools#

Stability: 1.1 - Active development

The node:inspector module provides an API for integrating with devtools that support Chrome DevTools Protocol. DevTools frontends connected to a running Node.js instance can capture protocol events emitted from the instance and display them accordingly to facilitate debugging. The following methods broadcast a protocol event to all connected frontends. The params passed to the methods can be optional, depending on the protocol.

// The `Network.requestWillBeSent` event will be fired.
inspector.Network.requestWillBeSent({
  requestId: 'request-id-1',
  timestamp: Date.now() / 1000,
  wallTime: Date.now(),
  request: {
    url: 'https://nodejs.org/en',
    method: 'GET',
  },
});
js

inspector.Network.dataReceived([params])#

This feature is only available with the --experimental-network-inspection flag enabled.

Broadcasts the Network.dataReceived event to connected frontends, or buffers the data if Network.streamResourceContent command was not invoked for the given request yet.

Also enables Network.getResponseBody command to retrieve the response data.

inspector.Network.dataSent([params])#

This feature is only available with the --experimental-network-inspection flag enabled.

Enables Network.getRequestPostData command to retrieve the request data.

inspector.Network.requestWillBeSent([params])#

This feature is only available with the --experimental-network-inspection flag enabled.

Broadcasts the Network.requestWillBeSent event to connected frontends. This event indicates that the application is about to send an HTTP request.

inspector.Network.responseReceived([params])#

This feature is only available with the --experimental-network-inspection flag enabled.

Broadcasts the Network.responseReceived event to connected frontends. This event indicates that HTTP response is available.

inspector.Network.loadingFinished([params])#

This feature is only available with the --experimental-network-inspection flag enabled.

Broadcasts the Network.loadingFinished event to connected frontends. This event indicates that HTTP request has finished loading.

inspector.Network.loadingFailed([params])#

This feature is only available with the --experimental-network-inspection flag enabled.

Broadcasts the Network.loadingFailed event to connected frontends. This event indicates that HTTP request has failed to load.

inspector.Network.webSocketCreated([params])#

This feature is only available with the --experimental-network-inspection flag enabled.

Broadcasts the Network.webSocketCreated event to connected frontends. This event indicates that a WebSocket connection has been initiated.

inspector.Network.webSocketHandshakeResponseReceived([params])#

This feature is only available with the --experimental-network-inspection flag enabled.

Broadcasts the Network.webSocketHandshakeResponseReceived event to connected frontends. This event indicates that the WebSocket handshake response has been received.

inspector.Network.webSocketClosed([params])#

This feature is only available with the --experimental-network-inspection flag enabled.

Broadcasts the Network.webSocketClosed event to connected frontends. This event indicates that a WebSocket connection has been closed.

inspector.NetworkResources.put#

Stability: 1.1 - Active Development

This feature is only available with the --experimental-inspector-network-resource flag enabled.

The inspector.NetworkResources.put method is used to provide a response for a loadNetworkResource request issued via the Chrome DevTools Protocol (CDP). This is typically triggered when a source map is specified by URL, and a DevTools frontend—such as Chrome—requests the resource to retrieve the source map.

This method allows developers to predefine the resource content to be served in response to such CDP requests.

const inspector = require('node:inspector');
// By preemptively calling put to register the resource, a source map can be resolved when
// a loadNetworkResource request is made from the frontend.
async function setNetworkResources() {
  const mapUrl = 'http://localhost:3000/dist/app.js.map';
  const tsUrl = 'http://localhost:3000/src/app.ts';
  const distAppJsMap = await fetch(mapUrl).then((res) => res.text());
  const srcAppTs = await fetch(tsUrl).then((res) => res.text());
  inspector.NetworkResources.put(mapUrl, distAppJsMap);
  inspector.NetworkResources.put(tsUrl, srcAppTs);
};
setNetworkResources().then(() => {
  require('./dist/app');
});
js

For more details, see the official CDP documentation: Network.loadNetworkResource

inspector.DOMStorage.domStorageItemAdded#

This feature is only available with the --experimental-storage-inspection flag enabled.

Broadcasts the DOMStorage.domStorageItemAdded event to connected frontends. This event indicates that a new item has been added to the storage.

inspector.DOMStorage.domStorageItemRemoved#

This feature is only available with the --experimental-storage-inspection flag enabled.

Broadcasts the DOMStorage.domStorageItemRemoved event to connected frontends. This event indicates that an item has been removed from the storage.

inspector.DOMStorage.domStorageItemUpdated#

This feature is only available with the --experimental-storage-inspection flag enabled.

Broadcasts the DOMStorage.domStorageItemUpdated event to connected frontends. This event indicates that a storage item has been updated.

inspector.DOMStorage.domStorageItemsCleared#

This feature is only available with the --experimental-storage-inspection flag enabled.

Broadcasts the DOMStorage.domStorageItemsCleared event to connected frontends. This event indicates that all items have been cleared from the storage.

inspector.DOMStorage.registerStorage#

This feature is only available with the --experimental-storage-inspection flag enabled.

Support of breakpoints#

The Chrome DevTools Protocol Debugger domain allows an inspector.Session to attach to a program and set breakpoints to step through the codes.

However, setting breakpoints with a same-thread inspector.Session, which is connected by session.connect(), should be avoided as the program being attached and paused is exactly the debugger itself. Instead, try connect to the main thread by session.connectToMainThread() and set breakpoints in a worker thread, or connect with a Debugger program over WebSocket connection.

Internationalization support#

Node.js has many features that make it easier to write internationalized programs. Some of them are:

Node.js and the underlying V8 engine use International Components for Unicode (ICU) to implement these features in native C/C++ code. The full ICU data set is provided by Node.js by default. However, due to the size of the ICU data file, several options are provided for customizing the ICU data set either when building or running Node.js.

Options for building Node.js#

To control how ICU is used in Node.js, four configure options are available during compilation. Additional details on how to compile Node.js are documented in BUILDING.md.

  • --with-intl=none/--without-intl
  • --with-intl=system-icu
  • --with-intl=small-icu
  • --with-intl=full-icu (default)

An overview of available Node.js and JavaScript features for each configure option:

Feature none system-icu small-icu full-icu
String.prototype.normalize() none (function is no-op) full full full
String.prototype.to*Case() full full full full
Intl none (object does not exist) partial/full (depends on OS) partial (English-only) full
String.prototype.localeCompare() partial (not locale-aware) full full full
String.prototype.toLocale*Case() partial (not locale-aware) full full full
Number.prototype.toLocaleString() partial (not locale-aware) partial/full (depends on OS) partial (English-only) full
Date.prototype.toLocale*String() partial (not locale-aware) partial/full (depends on OS) partial (English-only) full
Legacy URL Parser partial (no IDN support) full full full
WHATWG URL Parser partial (no IDN support) full full full
require('node:buffer').transcode() none (function does not exist) full full full
REPL partial (inaccurate line editing) full full full
require('node:util').TextDecoder partial (basic encodings support) partial/full (depends on OS) partial (Unicode-only) full
RegExp Unicode Property Escapes none (invalid RegExp error) full full full

The "(not locale-aware)" designation denotes that the function carries out its operation just like the non-Locale version of the function, if one exists. For example, under none mode, Date.prototype.toLocaleString()'s operation is identical to that of Date.prototype.toString().

Disable all internationalization features (none)#

If this option is chosen, ICU is disabled and most internationalization features mentioned above will be unavailable in the resulting node binary.

Build with a pre-installed ICU (system-icu)#

Node.js can link against an ICU build already installed on the system. In fact, most Linux distributions already come with ICU installed, and this option would make it possible to reuse the same set of data used by other components in the OS.

Functionalities that only require the ICU library itself, such as String.prototype.normalize() and the WHATWG URL parser, are fully supported under system-icu. Features that require ICU locale data in addition, such as Intl.DateTimeFormat may be fully or partially supported, depending on the completeness of the ICU data installed on the system.

Embed a limited set of ICU data (small-icu)#

This option makes the resulting binary link against the ICU library statically, and includes a subset of ICU data (typically only the English locale) within the node executable.

Functionalities that only require the ICU library itself, such as String.prototype.normalize() and the WHATWG URL parser, are fully supported under small-icu. Features that require ICU locale data in addition, such as Intl.DateTimeFormat, generally only work with the English locale:

const january = new Date(9e8);
const english = new Intl.DateTimeFormat('en', { month: 'long' });
const spanish = new Intl.DateTimeFormat('es', { month: 'long' });

console.log(english.format(january));
// Prints "January"
console.log(spanish.format(january));
// Prints either "M01" or "January" on small-icu, depending on the user’s default locale
// Should print "enero"
js

This mode provides a balance between features and binary size.

Providing ICU data at runtime#

If the small-icu option is used, one can still provide additional locale data at runtime so that the JS methods would work for all ICU locales. Assuming the data file is stored at /runtime/directory/with/dat/file, it can be made available to ICU through either:

  • The --with-icu-default-data-dir configure option:

    ./configure --with-icu-default-data-dir=/runtime/directory/with/dat/file --with-intl=small-icu
    
    bash

    This only embeds the default data directory path into the binary. The actual data file is going to be loaded at runtime from this directory path.

  • The NODE_ICU_DATA environment variable:

    env NODE_ICU_DATA=/runtime/directory/with/dat/file node
    
    bash
  • The --icu-data-dir CLI parameter:

    node --icu-data-dir=/runtime/directory/with/dat/file
    
    bash

When more than one of them is specified, the --icu-data-dir CLI parameter has the highest precedence, then the NODE_ICU_DATA environment variable, then the --with-icu-default-data-dir configure option.

ICU is able to automatically find and load a variety of data formats, but the data must be appropriate for the ICU version, and the file correctly named. The most common name for the data file is icudtX[bl].dat, where X denotes the intended ICU version, and b or l indicates the system's endianness. Node.js would fail to load if the expected data file cannot be read from the specified directory. The name of the data file corresponding to the current Node.js version can be computed with:

`icudt${process.versions.icu.split('.')[0]}${os.endianness()[0].toLowerCase()}.dat`;
js

Check "ICU Data" article in the ICU User Guide for other supported formats and more details on ICU data in general.

The full-icu npm module can greatly simplify ICU data installation by detecting the ICU version of the running node executable and downloading the appropriate data file. After installing the module through npm i full-icu, the data file will be available at ./node_modules/full-icu. This path can be then passed either to NODE_ICU_DATA or --icu-data-dir as shown above to enable full Intl support.

Embed the entire ICU (full-icu)#

This option makes the resulting binary link against ICU statically and include a full set of ICU data. A binary created this way has no further external dependencies and supports all locales, but might be rather large. This is the default behavior if no --with-intl flag is passed. The official binaries are also built in this mode.

Detecting internationalization support#

To verify that ICU is enabled at all (system-icu, small-icu, or full-icu), simply checking the existence of Intl should suffice:

const hasICU = typeof Intl === 'object';
js

Alternatively, checking for process.versions.icu, a property defined only when ICU is enabled, works too:

const hasICU = typeof process.versions.icu === 'string';
js

To check for support for a non-English locale (i.e. full-icu or system-icu), Intl.DateTimeFormat can be a good distinguishing factor:

const hasFullICU = (() => {
  try {
    const january = new Date(9e8);
    const spanish = new Intl.DateTimeFormat('es', { month: 'long' });
    return spanish.format(january) === 'enero';
  } catch (err) {
    return false;
  }
})();
js

For more verbose tests for Intl support, the following resources may be found to be helpful:

  • btest402: Generally used to check whether Node.js with Intl support is built correctly.
  • Test262: ECMAScript's official conformance test suite includes a section dedicated to ECMA-402.

Iterable Streams#

Stability: 1 - Experimental – Enable this API with the --experimental-stream-iter CLI flag.

The node:stream/iter module provides a streaming API built on iterables rather than the event-driven Readable/Writable/Transform class hierarchy, or the Web Streams ReadableStream/WritableStream/TransformStream interfaces.

Streams are represented as <AsyncIterable> (async) or <Iterable> (sync). There are no base classes to extend -- any object implementing the iterable protocol can participate. Transforms are plain functions or objects with a transform method.

Data flows in batches (<Uint8Array>[] per iteration) to amortize the cost of async operations.

import { from, pull, text } from 'node:stream/iter';
import { compressGzip, decompressGzip } from 'node:zlib/iter';

// Compress and decompress a string
const compressed = pull(from('Hello, world!'), compressGzip());
const result = await text(pull(compressed, decompressGzip()));
console.log(result); // 'Hello, world!'
const { from, pull, text } = require('node:stream/iter');
const { compressGzip, decompressGzip } = require('node:zlib/iter');

async function run() {
  // Compress and decompress a string
  const compressed = pull(from('Hello, world!'), compressGzip());
  const result = await text(pull(compressed, decompressGzip()));
  console.log(result); // 'Hello, world!'
}

run().catch(console.error);
javascript
import { open } from 'node:fs/promises';
import { text, pipeTo } from 'node:stream/iter';
import { compressGzip, decompressGzip } from 'node:zlib/iter';

// Read a file, compress, write to another file
const src = await open('input.txt', 'r');
const dst = await open('output.gz', 'w');
await pipeTo(src.pull(), compressGzip(), dst.writer({ autoClose: true }));
await src.close();

// Read it back
const gz = await open('output.gz', 'r');
console.log(await text(gz.pull(decompressGzip(), { autoClose: true })));
const { open } = require('node:fs/promises');
const { text, pipeTo } = require('node:stream/iter');
const { compressGzip, decompressGzip } = require('node:zlib/iter');

async function run() {
  // Read a file, compress, write to another file
  const src = await open('input.txt', 'r');
  const dst = await open('output.gz', 'w');
  await pipeTo(src.pull(), compressGzip(), dst.writer({ autoClose: true }));
  await src.close();

  // Read it back
  const gz = await open('output.gz', 'r');
  console.log(await text(gz.pull(decompressGzip(), { autoClose: true })));
}

run().catch(console.error);
javascript

Concepts#

Byte streams#

All data in this API is represented as <Uint8Array> bytes. Strings are automatically UTF-8 encoded when passed to from(), push(), or pipeTo(). This removes ambiguity around encodings and enables zero-copy transfers between streams and native code.

Batching#

Each iteration yields a batch -- an <Array> of <Uint8Array> chunks (<Uint8Array>[]). Batching amortizes the cost of await and <Promise> creation across multiple chunks. A consumer that processes one chunk at a time can simply iterate the inner array:

for await (const batch of source) {
  for (const chunk of batch) {
    handle(chunk);
  }
}
async function run() {
  for await (const batch of source) {
    for (const chunk of batch) {
      handle(chunk);
    }
  }
}
javascript

Transforms#

Transforms come in two forms:

  • Stateless -- a function (chunks, options) => result called once per batch. Receives Uint8Array[] (or null as the flush signal) and an options object. Returns <Uint8Array>[] | <null> | <Iterable>.

  • Stateful -- an object { transform(source, options) } where transform is a generator (sync or async) that receives the entire upstream iterable and an options object, and yields output. This form is used for compression, encryption, and any transform that needs to buffer across batches.

Both forms receive an options parameter with the following property:

  • options.signal <AbortSignal> An AbortSignal that fires when the pipeline is cancelled, encounters an error, or the consumer stops reading. Transforms can check signal.aborted or listen for the 'abort' event to perform early cleanup.

The flush signal (null) is sent after the source ends, giving transforms a chance to emit trailing data (e.g., compression footers).

// Stateless: uppercase transform
const upper = (chunks) => {
  if (chunks === null) return null; // flush
  return chunks.map((c) => new TextEncoder().encode(
    new TextDecoder().decode(c).toUpperCase(),
  ));
};

// Stateful: line splitter
const lines = {
  transform: async function*(source) {
    let partial = '';
    for await (const chunks of source) {
      if (chunks === null) {
        if (partial) yield [new TextEncoder().encode(partial)];
        continue;
      }
      for (const chunk of chunks) {
        const str = partial + new TextDecoder().decode(chunk);
        const parts = str.split('\n');
        partial = parts.pop();
        for (const line of parts) {
          yield [new TextEncoder().encode(`${line}\n`)];
        }
      }
    }
  },
};
js

Pull vs. push#

The API supports two models:

  • Pull -- data flows on demand. pull() and pullSync() create lazy pipelines that only read from the source when the consumer iterates.

  • Push -- data is written explicitly. push() creates a writer/readable pair with backpressure. The writer pushes data in; the readable is consumed as an async iterable.

Backpressure#

Pull streams have natural backpressure -- the consumer drives the pace, so the source is never read faster than the consumer can process. Push streams need explicit backpressure because the producer and consumer run independently. The budget and backpressure options on push(), broadcast(), and share() control how this works.

The two-buffer model#

Push streams use a two-part buffering system. Think of it like a bucket (buffer) being filled through a hose (pending writes), with a float valve that closes when the bucket is full:

                          budget (e.g., 16384)
                                 |
    Producer                     v
       |                    +---------+
       v                    |         |
  [ write() ] ----+    +--->| buffer  |---> Consumer pulls
  [ write() ]     |    |    | (bucket)|     for await (...)
  [ write() ]     v    |    +---------+
              +--------+         ^
              | pending|         |
              | writes |    float valve
              | (hose) |    (backpressure)
              +--------+
                   ^
                   |
          'strict' mode limits this too!
text
  • Buffer (the bucket) -- data ready for the consumer, capped at budget bytes. When the consumer pulls, it drains all buffered data at once into a single batch.

  • Pending writes (the hose) -- writes waiting for buffer space. After the consumer drains, pending writes are promoted into the now-empty buffer and their promises settle.

How each policy uses these buffers:

Policy Buffer limit Pending writes limit
'strict' budget 1
'unbounded' budget Unbounded
'drop-oldest' budget N/A (never waits)
'drop-newest' budget N/A (never waits)
Strict (default)#

Strict mode catches "fire-and-forget" patterns where the producer calls write() without awaiting, which would cause unbounded memory growth. It limits the buffer to budget bytes and the pending writes queue to a single entry.

If you properly await each write, you can only ever have one pending write at a time (yours), so you never hit the pending writes limit. Unawaited writes accumulate in the pending queue and throw once it overflows:

import { push, text } from 'node:stream/iter';

const { writer, readable } = push({ budget: 16384 });

// Consumer must run concurrently -- without it, the first write
// that fills the buffer blocks the producer forever.
const consuming = text(readable);

// GOOD: awaited writes. The producer waits for the consumer to
// make room when the buffer is full.
for (const item of dataset) {
  await writer.write(item);
}
await writer.end();
console.log(await consuming);
const { push, text } = require('node:stream/iter');

async function run() {
  const { writer, readable } = push({ budget: 16384 });

  // Consumer must run concurrently -- without it, the first write
  // that fills the buffer blocks the producer forever.
  const consuming = text(readable);

  // GOOD: awaited writes. The producer waits for the consumer to
  // make room when the buffer is full.
  for (const item of dataset) {
    await writer.write(item);
  }
  await writer.end();
  console.log(await consuming);
}

run().catch(console.error);
javascript

Forgetting to await will eventually throw:

// BAD: fire-and-forget. Strict mode throws once both buffers fill.
for (const item of dataset) {
  writer.write(item); // Not awaited -- queues without bound
}
// --> throws "Backpressure violation: too many pending writes"
js
Unbounded#

Unbounded mode caps buffered bytes at budget but places no limit on the pending writes queue. Awaited writes block until the consumer makes room, just like strict mode. The difference is that unawaited writes silently queue forever instead of throwing -- a potential memory leak if the producer forgets to await.

This is the mode that existing Node.js classic streams and Web Streams default to. Use it when you control the producer and know it awaits properly, or when migrating code from those APIs.

import { push, text } from 'node:stream/iter';

const { writer, readable } = push({
  budget: 16384,
  backpressure: 'unbounded',
});

const consuming = text(readable);

// Safe -- awaited writes block until the consumer reads.
for (const item of dataset) {
  await writer.write(item);
}
await writer.end();
console.log(await consuming);
const { push, text } = require('node:stream/iter');

async function run() {
  const { writer, readable } = push({
    budget: 16384,
    backpressure: 'unbounded',
  });

  const consuming = text(readable);

  // Safe -- awaited writes block until the consumer reads.
  for (const item of dataset) {
    await writer.write(item);
  }
  await writer.end();
  console.log(await consuming);
}

run().catch(console.error);
javascript
Drop-oldest#

Writes never wait. When the slots buffer is full, the oldest buffered chunk is evicted to make room for the incoming write. The consumer always sees the most recent data. Useful for live feeds, telemetry, or any scenario where stale data is less valuable than current data.

import { push } from 'node:stream/iter';

// Keep only the most recent ~16 KB of readings
const { writer, readable } = push({
  budget: 16384,
  backpressure: 'drop-oldest',
});
const { push } = require('node:stream/iter');

// Keep only the most recent ~16 KB of readings
const { writer, readable } = push({
  budget: 16384,
  backpressure: 'drop-oldest',
});
javascript
Drop-newest#

Writes never wait. When the slots buffer is full, the incoming write is silently discarded. The consumer processes what is already buffered without being overwhelmed by new data. Useful for rate-limiting or shedding load under pressure.

import { push } from 'node:stream/iter';

// Accept up to 16 KB of buffered data; discard anything beyond that
const { writer, readable } = push({
  budget: 16384,
  backpressure: 'drop-newest',
});
const { push } = require('node:stream/iter');

// Accept up to 16 KB of buffered data; discard anything beyond that
const { writer, readable } = push({
  budget: 16384,
  backpressure: 'drop-newest',
});
javascript

Writer interface#

A writer is any object conforming to the Writer interface. Only write() is required; all other methods are optional.

Writer arguments use Web IDL conversion semantics. A non-Uint8Array chunk is converted to a USVString and then UTF-8 encoded. writev() and writevSync() accept any iterable object whose values can be converted to chunks. Writer option dictionaries treat null as an empty dictionary and ignore unknown members.

Each async method has a synchronous *Sync counterpart designed for a try-fallback pattern: attempt the fast synchronous path first, and fall back to the async version only when the synchronous call indicates it could not complete:

if (!writer.writeSync(chunk)) await writer.write(chunk);
if (!writer.writevSync(chunks)) await writer.writev(chunks);
if (writer.endSync() < 0) await writer.end();
writer.fail(err);  // Always synchronous, no fallback needed
mjs
writer.canWrite#

Returns true if the slots buffer has physical capacity (buffered data is below the configured byte budget), false if the budget is exhausted, or null if the writer is closed or the consumer has disconnected.

This reports physical capacity independently of the backpressure policy. With 'drop-oldest' or 'drop-newest', writes still complete when this is false by evicting buffered data or discarding the incoming data, respectively.

This is a hint, not a guarantee: the state can change between the check and the write. Use ondrain() to wait for capacity rather than polling.

writer.end([options])#
  • options <Object>
    • signal <AbortSignal> Cancel just this operation. The signal cancels only the pending end() call; it does not fail the writer itself.
  • Returns: <Promise> Fulfills with the total number of bytes written.

Signals that no more data will be written. Writes already waiting for buffer space remain ordered before the end of the stream, while later writes fail. If data is outstanding, the returned promise fulfills after the consumer pulls done: true beyond the final batch. If no data is buffered or pending, the writer closes immediately.

writer.endSync()#
  • Returns: <number> Total bytes written, or -1 if ending cannot complete synchronously.

Synchronous variant of writer.end(). A return value of -1 only indicates that the operation could not complete synchronously; no assumption can be made about whether closing has started or why it could not complete. Use the try-fallback pattern to await completion:

const result = writer.endSync();
if (result < 0) {
  writer.end();
}
cjs
writer.fail([reason])#

Put the writer into a terminal error state. If the writer is already closed or errored, this is a no-op. Unlike write() and end(), fail() is unconditionally synchronous because failing a writer is a pure state transition with no async work to perform. The reason is stored and propagated without modification. If omitted, the reason is undefined.

writer[Symbol.asyncDispose]()#

If the writer is open, calls writer.fail(). If the writer is closing after end() or endSync(), waits for buffered data to drain. If the writer is already closed or errored, resolves immediately.

writer.write(chunk[, options])#
  • chunk <Uint8Array> | <string>
  • options <Object>
    • signal <AbortSignal> Cancel just this write operation. The signal cancels only the pending write() call; it does not fail the writer itself.
  • Returns: <Promise> Fulfills with undefined when buffer space is available.

Write a chunk.

writer.writeSync(chunk)#

Synchronous write. Does not block; returns false if backpressure is active.

writer.writev(chunks[, options])#

Write multiple chunks as a single batch.

writer.writevSync(chunks)#

Synchronous batch write.

The stream/iter module#

Most functions are available both as named exports and as properties of the Stream namespace object. The classic stream adapters (fromReadable(), fromWritable(), toReadable(), toReadableSync(), and toWritable()) and the static helper objects (Broadcast, Share, and SyncShare) are named exports only.

// Named exports
import { from, pull, bytes, Stream } from 'node:stream/iter';

// Namespace access
Stream.from('hello');
mjs

Options dictionaries defined by the Iterable Streams API use Web IDL conversion semantics. null is treated as an empty dictionary, unknown members are ignored, and known members are converted to their declared types before the operation runs. Conversion failures use Node.js error codes such as ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, and ERR_OUT_OF_RANGE.

// Named exports
const { from, pull, bytes, Stream } = require('node:stream/iter');

// Namespace access
Stream.from('hello');
cjs

Including the node: prefix on the module specifier is optional.

Sources#

from(input)#

Create an async byte stream from the given input. Strings are UTF-8 encoded. ArrayBuffer and ArrayBufferView values are wrapped as Uint8Array. Arrays and iterables in input are recursively flattened and normalized. Flattened values may be split across implementation-defined bounded batches.

Objects implementing Symbol.for('Stream.toAsyncStreamable') or Symbol.for('Stream.toStreamable') are converted via those protocols. The toAsyncStreamable protocol takes precedence over toStreamable, which takes precedence over the iteration protocols (Symbol.asyncIterator, Symbol.iterator).

import { Buffer } from 'node:buffer';
import { from, text } from 'node:stream/iter';

console.log(await text(from('hello')));       // 'hello'
console.log(await text(from(Buffer.from('hello')))); // 'hello'
const { Buffer } = require('node:buffer');
const { from, text } = require('node:stream/iter');

async function run() {
  console.log(await text(from('hello')));       // 'hello'
  console.log(await text(from(Buffer.from('hello')))); // 'hello'
}

run().catch(console.error);
javascript

fromSync(input)#

Synchronous version of from(). Returns a sync iterable. Cannot accept async iterables or promises. Objects implementing Symbol.for('Stream.toStreamable') are converted via that protocol (takes precedence over Symbol.iterator). The toAsyncStreamable protocol is ignored entirely.

import { fromSync, textSync } from 'node:stream/iter';

console.log(textSync(fromSync('hello'))); // 'hello'
const { fromSync, textSync } = require('node:stream/iter');

console.log(textSync(fromSync('hello'))); // 'hello'
javascript

Pipelines#

pipeTo(source[, ...transforms], writer[, options])#

  • source <AsyncIterable> | <Iterable> The data source.
  • ...transforms <Function> | <Object> Zero or more transforms to apply.
  • writer <Object> Destination with write(chunk) method.
  • options <Object>
    • signal <AbortSignal> Abort the pipeline. Aborting fails the destination writer unless preventFail is true.
    • preventClose <boolean> If true, do not call writer.end() when the source ends. Default: false.
    • preventFail <boolean> If true, do not call writer.fail() on error. Default: false.
  • Returns: <Promise> Fulfills with the total number of bytes written.

Pipe a source through transforms into a writer. If the writer has a writev(chunks) method, entire batches are passed in a single call (enabling scatter/gather I/O).

If the writer implements the optional *Sync methods (writeSync, writevSync, endSync), pipeTo() will attempt to use the synchronous methods first as a fast path, and fall back to the async versions only when the sync methods indicate they cannot complete (e.g., backpressure or waiting for the next tick). fail() is always called synchronously.

import { from, pipeTo } from 'node:stream/iter';
import { compressGzip } from 'node:zlib/iter';
import { open } from 'node:fs/promises';

const fh = await open('output.gz', 'w');
const totalBytes = await pipeTo(
  from('Hello, world!'),
  compressGzip(),
  fh.writer({ autoClose: true }),
);
const { from, pipeTo } = require('node:stream/iter');
const { compressGzip } = require('node:zlib/iter');
const { open } = require('node:fs/promises');

async function run() {
  const fh = await open('output.gz', 'w');
  const totalBytes = await pipeTo(
    from('Hello, world!'),
    compressGzip(),
    fh.writer({ autoClose: true }),
  );
}

run().catch(console.error);
javascript

pipeToSync(source[, ...transforms], writer[, options])#

Synchronous version of pipeTo(). The source, all transforms, and the writer must be synchronous. Cannot accept async iterables or promises.

The writer must have the *Sync methods (writeSync, writevSync, endSync) and fail() for this to work.

pull(source[, ...transforms][, options])#

Create a lazy async pipeline. Source conversion and streamable protocol dispatch occur when pull() is called, but data is not read from source until the returned iterable is consumed. A signal that is already aborted is thrown synchronously after source conversion. Transforms are applied in order.

import { from, pull, text } from 'node:stream/iter';

const asciiUpper = (chunks) => {
  if (chunks === null) return null;
  return chunks.map((c) => {
    for (let i = 0; i < c.length; i++) {
      c[i] -= (c[i] >= 97 && c[i] <= 122) * 32;
    }
    return c;
  });
};

const result = pull(from('hello'), asciiUpper);
console.log(await text(result)); // 'HELLO'
const { from, pull, text } = require('node:stream/iter');

const asciiUpper = (chunks) => {
  if (chunks === null) return null;
  return chunks.map((c) => {
    for (let i = 0; i < c.length; i++) {
      c[i] -= (c[i] >= 97 && c[i] <= 122) * 32;
    }
    return c;
  });
};

async function run() {
  const result = pull(from('hello'), asciiUpper);
  console.log(await text(result)); // 'HELLO'
}

run().catch(console.error);
javascript

Using an AbortSignal:

import { pull } from 'node:stream/iter';

const ac = new AbortController();
const result = pull(source, transform, { signal: ac.signal });
ac.abort(); // Pipeline throws AbortError on next iteration
const { pull } = require('node:stream/iter');

const ac = new AbortController();
const result = pull(source, transform, { signal: ac.signal });
ac.abort(); // Pipeline throws AbortError on next iteration
javascript

pullSync(source[, ...transforms])#

Synchronous version of pull(). Source conversion and streamable protocol dispatch occur when pullSync() is called. All transforms must be synchronous.

Push streams#

push([...transforms][, options])#

  • ...transforms <Function> | <Object> Optional transforms applied to the readable side.
  • options <Object>
    • budget <number> Maximum number of buffered bytes before backpressure is applied. Must be >= 16384. Default: 16384.
    • backpressure <string> Backpressure policy: 'strict', 'unbounded', 'drop-oldest', or 'drop-newest'. Default: 'strict'.
    • signal <AbortSignal> Abort the stream. The signal remains active while buffered data drains after writer.end(); aborting during that time fails the writer and rejects the pending end() promise.
  • Returns: <Object>

Create a push stream with backpressure. The writer pushes data in; the readable side is consumed as an async iterable.

import { push, text } from 'node:stream/iter';

const { writer, readable } = push();

// Producer and consumer must run concurrently. With strict backpressure
// (the default), awaited writes block until the consumer reads.
const producing = (async () => {
  await writer.write('hello');
  await writer.write(' world');
  await writer.end();
})();

console.log(await text(readable)); // 'hello world'
await producing;
const { push, text } = require('node:stream/iter');

async function run() {
  const { writer, readable } = push();

  // Producer and consumer must run concurrently. With strict backpressure
  // (the default), awaited writes block until the consumer reads.
  const producing = (async () => {
    await writer.write('hello');
    await writer.write(' world');
    await writer.end();
  })();

  console.log(await text(readable)); // 'hello world'
  await producing;
}

run().catch(console.error);
javascript

The writer returned by push() conforms to the [Writer interface][].

Duplex channels#

duplex([options])#

  • options <Object>
    • budget <number> Buffer size in bytes for both directions. Must be >= 16384. Default: 16384.
    • backpressure <string> Policy for both directions. Default: 'strict'.
    • signal <AbortSignal> Cancellation signal for both channels.
    • a <Object> Options specific to the A-to-B direction. Overrides shared options.
    • b <Object> Options specific to the B-to-A direction. Overrides shared options.
  • Returns: <Array> A pair [channelA, channelB] of duplex channels.

Create a pair of connected duplex channels for bidirectional communication, similar to socketpair(). Data written to one channel's writer appears in the other channel's readable.

Each channel has:

  • writer — a [Writer interface][] object for sending data to the peer.
  • readable — an <AsyncIterable> for reading data from the peer.
  • close() — close this end of the channel (idempotent).
  • [Symbol.asyncDispose]() — async dispose support for await using.
import { duplex, text } from 'node:stream/iter';

const [client, server] = duplex();

// Server echoes back
const serving = (async () => {
  for await (const chunks of server.readable) {
    await server.writer.writev(chunks);
  }
  await server.writer.end();
})();

await client.writer.write('hello');
await client.writer.end();

console.log(await text(client.readable)); // 'hello'
await serving;
const { duplex, text } = require('node:stream/iter');

async function run() {
  const [client, server] = duplex();

  // Server echoes back
  const serving = (async () => {
    for await (const chunks of server.readable) {
      await server.writer.writev(chunks);
    }
    await server.writer.end();
  })();

  await client.writer.write('hello');
  await client.writer.end();

  console.log(await text(client.readable)); // 'hello'
  await serving;
}

run().catch(console.error);
javascript

Consumers#

array(source[, options])#

Collect all chunks as an array of Uint8Array values (without concatenating).

arrayBuffer(source[, options])#

Collect all bytes into an ArrayBuffer.

arrayBufferSync(source[, options])#

Synchronous version of arrayBuffer().

arraySync(source[, options])#

Synchronous version of array().

bytes(source[, options])#

Collect all bytes from a stream into a single Uint8Array.

import { from, bytes } from 'node:stream/iter';

const data = await bytes(from('hello'));
console.log(data); // Uint8Array(5) [ 104, 101, 108, 108, 111 ]
const { from, bytes } = require('node:stream/iter');

async function run() {
  const data = await bytes(from('hello'));
  console.log(data); // Uint8Array(5) [ 104, 101, 108, 108, 111 ]
}

run().catch(console.error);
javascript

bytesSync(source[, options])#

Synchronous version of bytes().

text(source[, options])#

Collect all bytes and decode as text.

import { from, text } from 'node:stream/iter';

console.log(await text(from('hello'))); // 'hello'
const { from, text } = require('node:stream/iter');

async function run() {
  console.log(await text(from('hello'))); // 'hello'
}

run().catch(console.error);
javascript

textSync(source[, options])#

Synchronous version of text().

Utilities#

ondrain(drainable)#

Wait for a drainable writer to regain physical buffer capacity. Returns null if the object does not implement the drainable protocol, or a promise that fulfills with true when buffered data falls below the byte budget.

For writers using 'drop-oldest' or 'drop-newest', this waits for physical capacity even though writes do not block. This allows producers to avoid data loss by waiting before writing.

import { push, ondrain, text } from 'node:stream/iter';

const { writer, readable } = push({ budget: 16384 });
const chunk = new Uint8Array(8192);  // 8 KB
writer.writeSync(chunk);
writer.writeSync(chunk);  // 16 KB total -- buffer full

// Start consuming so the buffer can actually drain
const consuming = text(readable);

// Buffer is full -- wait for drain
const canWrite = await ondrain(writer);
if (canWrite) {
  await writer.write('c');
}
await writer.end();
await consuming;
const { push, ondrain, text } = require('node:stream/iter');

async function run() {
  const { writer, readable } = push({ budget: 16384 });
  const chunk = new Uint8Array(8192);  // 8 KB
  writer.writeSync(chunk);
  writer.writeSync(chunk);  // 16 KB total -- buffer full

  // Start consuming so the buffer can actually drain
  const consuming = text(readable);

  // Buffer is full -- wait for drain
  const canWrite = await ondrain(writer);
  if (canWrite) {
    await writer.write('c');
  }
  await writer.end();
  await consuming;
}

run().catch(console.error);
javascript

merge(...sources[, options])#

Merge multiple async iterables by yielding batches in temporal order (whichever source produces data first). All sources are consumed concurrently.

import { from, merge, text } from 'node:stream/iter';

const merged = merge(from('hello '), from('world'));
console.log(await text(merged)); // Order depends on timing
const { from, merge, text } = require('node:stream/iter');

async function run() {
  const merged = merge(from('hello '), from('world'));
  console.log(await text(merged)); // Order depends on timing
}

run().catch(console.error);
javascript

tap(callback)#

  • callback <Function> (chunks) => void Called with each batch and with null when the source ends.
  • Returns: <Function> A stateless transform.

Create a pass-through transform that observes batches without modifying them. Useful for logging, metrics, or debugging.

import { from, pull, text, tap } from 'node:stream/iter';

const result = pull(
  from('hello'),
  tap((chunks) => {
    if (chunks !== null) console.log('Batch size:', chunks.length);
  }),
);
console.log(await text(result));
const { from, pull, text, tap } = require('node:stream/iter');

async function run() {
  const result = pull(
    from('hello'),
    tap((chunks) => {
      if (chunks !== null) console.log('Batch size:', chunks.length);
    }),
  );
  console.log(await text(result));
}

run().catch(console.error);
javascript

tap() intentionally does not prevent in-place modification of the chunks by the tapping callback; but return values are ignored.

tapSync(callback)#

Synchronous version of tap().

Multi-consumer#

broadcast([options])#

Create a push-model multi-consumer broadcast channel. A single writer pushes data to multiple consumers. Each consumer has an independent cursor into a shared buffer.

import { broadcast, text } from 'node:stream/iter';

const { writer, broadcast: bc } = broadcast();

// Create consumers before writing
const c1 = bc.push();  // Consumer 1
const c2 = bc.push();  // Consumer 2

// Producer and consumers must run concurrently. Awaited writes
// block when the buffer fills until consumers read.
const producing = (async () => {
  await writer.write('hello');
  await writer.end();
})();

const [r1, r2] = await Promise.all([text(c1), text(c2)]);
console.log(r1); // 'hello'
console.log(r2); // 'hello'
await producing;
const { broadcast, text } = require('node:stream/iter');

async function run() {
  const { writer, broadcast: bc } = broadcast();

  // Create consumers before writing
  const c1 = bc.push();  // Consumer 1
  const c2 = bc.push();  // Consumer 2

  // Producer and consumers must run concurrently. Awaited writes
  // block when the buffer fills until consumers read.
  const producing = (async () => {
    await writer.write('hello');
    await writer.end();
  })();

  const [r1, r2] = await Promise.all([text(c1), text(c2)]);
  console.log(r1); // 'hello'
  console.log(r2); // 'hello'
  await producing;
}

run().catch(console.error);
javascript
broadcast.cancel([reason])#

Cancel the broadcast. If reason is provided, all consumers reject with that exact reason. If it is omitted, consumers complete normally.

broadcast.consumerCount#

The number of active consumers.

broadcast.push([...transforms][, options])#

Create a new consumer. Optional transforms are applied to this consumer's view of the data.

broadcast[Symbol.dispose]()#

Alias for broadcast.cancel().

Broadcast.from(input[, options])#

Create a <BroadcastChannel> from an existing source. The source is consumed automatically and pushed to all subscribers.

share(source[, options])#

Create a pull-model multi-consumer shared stream. Unlike broadcast(), the source is only read when a consumer pulls. Multiple consumers share a single buffer.

import { from, share, text } from 'node:stream/iter';

const shared = share(from('hello'));

const c1 = shared.pull();
const c2 = shared.pull();

// Consume concurrently to avoid deadlock with small buffers.
const [r1, r2] = await Promise.all([text(c1), text(c2)]);
console.log(r1); // 'hello'
console.log(r2); // 'hello'
const { from, share, text } = require('node:stream/iter');

async function run() {
  const shared = share(from('hello'));

  const c1 = shared.pull();
  const c2 = shared.pull();

  // Consume concurrently to avoid deadlock with small buffers.
  const [r1, r2] = await Promise.all([text(c1), text(c2)]);
  console.log(r1); // 'hello'
  console.log(r2); // 'hello'
}

run().catch(console.error);
javascript

Class: Share#

Static method: Share.from(input[, options])#

Create a <Share> from an existing source.

share.cancel([reason])#

Cancel the share. If reason is provided, all consumers reject with that exact reason. If it is omitted, consumers complete normally.

share.consumerCount#

The number of active consumers.

share.pull([...transforms][, options])#

Create a new consumer of the shared source.

share[Symbol.dispose]()#

Alias for share.cancel().

Interface: Shareable#

sharable[Symbol.for('Stream.shareProtocol')]#

Interface: SyncShareable#

sharable[Symbol.for('Stream.shareSyncProtocol')]#

shareSync(source[, options])#

Synchronous version of share().

Because there is no way to wait in a synchronous context, 'unbounded' is not supported and throws ERR_INVALID_ARG_VALUE. With 'drop-newest', a consumer that reaches the end of the buffer while the budget is exhausted discards a single entry from the source and then returns { done: true } without a value; the consumer is not detached, so it can resume once the slowest consumer advances and releases budget.

Class: SyncShare#

Static method: SyncShare.fromSync(input[, options])#
share.cancel([reason])#

Cancel the share. If reason is provided, all consumers throw that exact reason. If it is omitted, consumers complete normally.

share.consumerCount#

The number of active consumers.

share.pull([...transforms])#

Create a new consumer of the shared source.

share[Symbol.dispose]()#

Alias for share.cancel().

Compression and decompression transforms#

Compression and decompression transforms for use with pull(), pullSync(), pipeTo(), and pipeToSync() are available via the node:zlib/iter module. See the node:zlib/iter documentation for details.

Classic stream interop#

These utility functions bridge between classic stream.Readable/stream.Writable streams and the stream/iter API.

Both fromReadable() and fromWritable() accept duck-typed objects -- they do not require the input to extend stream.Readable or stream.Writable directly. The minimum contract is described below for each function.

fromReadable(readable)#

Stability: 1 - Experimental

Converts a classic Readable stream (or duck-typed equivalent) into a stream/iter async iterable source that can be passed to from(), pull(), text(), etc.

If the object implements the toAsyncStreamable protocol (as stream.Readable does), that protocol is used. Otherwise, the function duck-types on read(), pipe(), destroy(), on(), and removeListener() (EventEmitter) and wraps the stream with a batched async iterator.

The result is cached per instance -- calling fromReadable() twice with the same stream returns the same iterable.

For object-mode or encoded Readable streams, chunks are automatically normalized to Uint8Array.

import { Readable } from 'node:stream';
import { fromReadable, text } from 'node:stream/iter';

const readable = new Readable({
  read() { this.push('hello world'); this.push(null); },
});

const result = await text(fromReadable(readable));
console.log(result); // 'hello world'
const { Readable } = require('node:stream');
const { fromReadable, text } = require('node:stream/iter');

const readable = new Readable({
  read() { this.push('hello world'); this.push(null); },
});

async function run() {
  const result = await text(fromReadable(readable));
  console.log(result); // 'hello world'
}
run();
javascript

fromWritable(writable[, options])#

Stability: 1 - Experimental

  • writable <stream.Writable> | <Object> A classic Writable stream or a compatible object with write(), end(), destroy(), on(), and removeListener() methods.
  • options <Object>
    • backpressure <string> Backpressure policy. Default: 'strict'.
      • 'strict' -- one write may wait while the buffer is full. Further writes are rejected until it is accepted or canceled.
      • 'unbounded' -- writes are queued while the buffer is full. Recommended for use with pipeTo().
      • 'drop-newest' -- writes are silently discarded when the buffer is full.
      • 'drop-oldest' -- not supported. Throws ERR_INVALID_ARG_VALUE.
  • Returns: <Object> A stream/iter Writer adapter.

Creates a stream/iter Writer adapter from a classic Writable stream (or duck-typed equivalent). The adapter can be passed to pipeTo() as a destination.

Since all writes on a classic Writable are fundamentally asynchronous, the synchronous Writer methods (writeSync, writevSync, endSync) always return false or -1, deferring to the async path. A queued write() or writev() can be canceled with its options.signal before it reaches the classic Writable.

If writer.fail(reason) receives a non-Error reason, the classic Writable is destroyed with an ERR_FALSY_VALUE_REJECTION or ERR_OPERATION_FAILED error. Its reason property contains the original value, which remains the Writer's stored failure reason.

The result is cached per instance and backpressure policy -- calling fromWritable() twice with the same stream and backpressure option returns the same Writer.

For duck-typed streams that do not expose writableHighWaterMark, writableLength, or similar properties, sensible defaults are used. Object-mode writables (if detectable) are rejected since the Writer interface is bytes-only.

import { Writable } from 'node:stream';
import { from, fromWritable, pipeTo } from 'node:stream/iter';

const writable = new Writable({
  write(chunk, encoding, cb) { console.log(chunk.toString()); cb(); },
});

await pipeTo(from('hello world'),
             fromWritable(writable, { backpressure: 'unbounded' }));
const { Writable } = require('node:stream');
const { from, fromWritable, pipeTo } = require('node:stream/iter');

async function run() {
  const writable = new Writable({
    write(chunk, encoding, cb) { console.log(chunk.toString()); cb(); },
  });

  await pipeTo(from('hello world'),
               fromWritable(writable, { backpressure: 'unbounded' }));
}
run();
javascript

toReadable(source[, options])#

Stability: 1 - Experimental

Creates a byte-mode stream.Readable from the source (the native batch format used by the stream/iter API). Each Uint8Array in a yielded batch is pushed as a separate chunk into the Readable.

Classic streams cannot represent arbitrary values as emitted errors. A non-Error reason is wrapped in an ERR_FALSY_VALUE_REJECTION or ERR_OPERATION_FAILED error whose reason property contains the original value.

import { createWriteStream } from 'node:fs';
import { from, pull, toReadable } from 'node:stream/iter';
import { compressGzip } from 'node:zlib/iter';

const source = pull(from('hello world'), compressGzip());
const readable = toReadable(source);

readable.pipe(createWriteStream('output.gz'));
const { createWriteStream } = require('node:fs');
const { from, pull, toReadable } = require('node:stream/iter');
const { compressGzip } = require('node:zlib/iter');

const source = pull(from('hello world'), compressGzip());
const readable = toReadable(source);

readable.pipe(createWriteStream('output.gz'));
javascript

toReadableSync(source[, options])#

Stability: 1 - Experimental

Creates a byte-mode stream.Readable from the source. The _read() method pulls from the iterator synchronously, so data is available immediately via readable.read().

import { fromSync, toReadableSync } from 'node:stream/iter';

const source = fromSync('hello world');
const readable = toReadableSync(source);

console.log(readable.read().toString()); // 'hello world'
const { fromSync, toReadableSync } = require('node:stream/iter');

const source = fromSync('hello world');
const readable = toReadableSync(source);

console.log(readable.read().toString()); // 'hello world'
javascript

toWritable(writer)#

Stability: 1 - Experimental

  • writer <Object> A stream/iter Writer. Only the write() method is required; end(), fail(), writeSync(), writevSync(), endSync(), and writev() are optional.
  • Returns: <stream.Writable>

Creates a classic stream.Writable backed by a stream/iter Writer.

Each _write() / _writev() call attempts the Writer's synchronous method first (writeSync / writevSync), falling back to the async method if the sync path returns false. Similarly, _final() tries endSync() before end(). When the sync path succeeds, the callback is deferred via queueMicrotask to preserve the async resolution contract.

Classic stream callbacks cannot represent arbitrary values as errors. A non-Error reason is wrapped in an ERR_FALSY_VALUE_REJECTION or ERR_OPERATION_FAILED error before it is passed to the callback. The error's reason property contains the original value.

Destroying the Writable before successful completion calls writer.fail(). If fail() is unavailable, Symbol.dispose or Symbol.asyncDispose is used when implemented by the Writer.

The Writable uses the default classic stream highWaterMark. Classic stream backpressure bounds writes waiting to reach the underlying Writer, while the Writer controls completion of the active _write() or _writev() operation.

import { push, toWritable } from 'node:stream/iter';

const { writer, readable } = push();
const writable = toWritable(writer);

writable.write('hello');
writable.end();
const { push, toWritable } = require('node:stream/iter');

const { writer, readable } = push();
const writable = toWritable(writer);

writable.write('hello');
writable.end();
javascript

Protocol symbols#

These well-known symbols allow third-party objects to participate in the streaming protocol without importing from node:stream/iter directly.

Stream.broadcastProtocol#

  • Value: Symbol.for('Stream.broadcastProtocol')

The value must be a function. When called by Broadcast.from(), it receives the options passed to Broadcast.from() and must return an object conforming to the <BroadcastChannel> interface. The implementation is fully custom -- it can manage consumers, buffering, and backpressure however it wants.

import {
  broadcast as createBroadcast,
  Broadcast,
  text,
} from 'node:stream/iter';

// This example defers to the built-in Broadcast, but a custom
// implementation could use any mechanism.
class MessageBus {
  #broadcast;
  #writer;

  constructor() {
    const { writer, broadcast } = createBroadcast();
    this.#writer = writer;
    this.#broadcast = broadcast;
  }

  [Symbol.for('Stream.broadcastProtocol')](options) {
    return this.#broadcast;
  }

  send(data) {
    this.#writer.write(new TextEncoder().encode(data));
  }

  close() {
    this.#writer.end();
  }
}

const bus = new MessageBus();
const broadcast = Broadcast.from(bus);
const consumer = broadcast.push();
bus.send('hello');
bus.close();
console.log(await text(consumer)); // 'hello'
const {
  broadcast: createBroadcast,
  Broadcast,
  text,
} = require('node:stream/iter');

// This example defers to the built-in Broadcast, but a custom
// implementation could use any mechanism.
class MessageBus {
  #broadcast;
  #writer;

  constructor() {
    const { writer, broadcast } = createBroadcast();
    this.#writer = writer;
    this.#broadcast = broadcast;
  }

  [Symbol.for('Stream.broadcastProtocol')](options) {
    return this.#broadcast;
  }

  send(data) {
    this.#writer.write(new TextEncoder().encode(data));
  }

  close() {
    this.#writer.end();
  }
}

const bus = new MessageBus();
const broadcast = Broadcast.from(bus);
const consumer = broadcast.push();
bus.send('hello');
bus.close();
text(consumer).then(console.log); // 'hello'
javascript

Stream.drainableProtocol#

  • Value: Symbol.for('Stream.drainableProtocol')

Implement to make a writer compatible with ondrain(). The method should return null if no backpressure, or a promise that fulfills with a truthy value when backpressure clears.

import { ondrain } from 'node:stream/iter';

class CustomWriter {
  #queue = [];
  #drain = null;
  #closed = false;
  [Symbol.for('Stream.drainableProtocol')]() {
    if (this.#closed) return null;
    if (this.#queue.length < 3) return Promise.resolve(true);
    this.#drain ??= Promise.withResolvers();
    return this.#drain.promise;
  }
  write(chunk) {
    this.#queue.push(chunk);
  }
  flush() {
    this.#queue.length = 0;
    this.#drain?.resolve(true);
    this.#drain = null;
  }
  close() {
    this.#closed = true;
  }
}
const writer = new CustomWriter();
const ready = ondrain(writer);
console.log(ready); // Promise { true } -- no backpressure
const { ondrain } = require('node:stream/iter');

class CustomWriter {
  #queue = [];
  #drain = null;
  #closed = false;

  [Symbol.for('Stream.drainableProtocol')]() {
    if (this.#closed) return null;
    if (this.#queue.length < 3) return Promise.resolve(true);
    this.#drain ??= Promise.withResolvers();
    return this.#drain.promise;
  }

  write(chunk) {
    this.#queue.push(chunk);
  }

  flush() {
    this.#queue.length = 0;
    this.#drain?.resolve(true);
    this.#drain = null;
  }

  close() {
    this.#closed = true;
  }
}

const writer = new CustomWriter();
const ready = ondrain(writer);
console.log(ready); // Promise { true } -- no backpressure
javascript

Stream.shareProtocol#

  • Value: Symbol.for('Stream.shareProtocol')

The value must be a function. When called by Share.from(), it receives the options passed to Share.from() and must return an object conforming to the <Share> interface. The implementation is fully custom -- it can manage the shared source, consumers, buffering, and backpressure however it wants.

import { share, Share, text } from 'node:stream/iter';

// This example defers to the built-in share(), but a custom
// implementation could use any mechanism.
class DataPool {
  #share;

  constructor(source) {
    this.#share = share(source);
  }

  [Symbol.for('Stream.shareProtocol')](options) {
    return this.#share;
  }
}

const pool = new DataPool(
  (async function* () {
    yield 'hello';
  })(),
);

const shared = Share.from(pool);
const consumer = shared.pull();
console.log(await text(consumer)); // 'hello'
const { share, Share, text } = require('node:stream/iter');

// This example defers to the built-in share(), but a custom
// implementation could use any mechanism.
class DataPool {
  #share;

  constructor(source) {
    this.#share = share(source);
  }

  [Symbol.for('Stream.shareProtocol')](options) {
    return this.#share;
  }
}

const pool = new DataPool(
  (async function* () {
    yield 'hello';
  })(),
);

const shared = Share.from(pool);
const consumer = shared.pull();
text(consumer).then(console.log); // 'hello'
javascript

Stream.shareSyncProtocol#

  • Value: Symbol.for('Stream.shareSyncProtocol')

The value must be a function. When called by SyncShare.fromSync(), it receives the options passed to SyncShare.fromSync() and must return an object conforming to the <SyncShare> interface. The implementation is fully custom -- it can manage the shared source, consumers, and buffering however it wants.

import { shareSync, SyncShare, textSync } from 'node:stream/iter';

// This example defers to the built-in shareSync(), but a custom
// implementation could use any mechanism.
class SyncDataPool {
  #share;

  constructor(source) {
    this.#share = shareSync(source);
  }

  [Symbol.for('Stream.shareSyncProtocol')](options) {
    return this.#share;
  }
}

const encoder = new TextEncoder();
const pool = new SyncDataPool(
  function* () {
    yield [encoder.encode('hello')];
  }(),
);

const shared = SyncShare.fromSync(pool);
const consumer = shared.pull();
console.log(textSync(consumer)); // 'hello'
const { shareSync, SyncShare, textSync } = require('node:stream/iter');

// This example defers to the built-in shareSync(), but a custom
// implementation could use any mechanism.
class SyncDataPool {
  #share;

  constructor(source) {
    this.#share = shareSync(source);
  }

  [Symbol.for('Stream.shareSyncProtocol')](options) {
    return this.#share;
  }
}

const encoder = new TextEncoder();
const pool = new SyncDataPool(
  function* () {
    yield [encoder.encode('hello')];
  }(),
);

const shared = SyncShare.fromSync(pool);
const consumer = shared.pull();
console.log(textSync(consumer)); // 'hello'
javascript

Stream.toAsyncStreamable#

  • Value: Symbol.for('Stream.toAsyncStreamable')

The value must be a function that converts the object into a streamable value. When the object is passed to from(), this method is called to produce the actual data. It may return any value that resolves to a string, Uint8Array, AsyncIterable, Iterable, or another streamable object.

import { from, text } from 'node:stream/iter';

class Greeting {
  #name;

  constructor(name) {
    this.#name = name;
  }

  [Symbol.for('Stream.toAsyncStreamable')]() {
    return `hello ${this.#name}`;
  }
}

const stream = from(new Greeting('world'));
console.log(await text(stream)); // 'hello world'
const { from, text } = require('node:stream/iter');

class Greeting {
  #name;

  constructor(name) {
    this.#name = name;
  }

  [Symbol.for('Stream.toAsyncStreamable')]() {
    return `hello ${this.#name}`;
  }
}

const stream = from(new Greeting('world'));
text(stream).then(console.log); // 'hello world'
javascript

Stream.toStreamable#

  • Value: Symbol.for('Stream.toStreamable')

The value must be a function that synchronously converts the object into a streamable value. When the object is passed to fromSync(), this method is called to produce the actual data. It must synchronously return a streamable value: a string, Uint8Array, or Iterable.

import { fromSync, textSync } from 'node:stream/iter';

class Greeting {
  #name;

  constructor(name) {
    this.#name = name;
  }

  [Symbol.for('Stream.toStreamable')]() {
    return `hello ${this.#name}`;
  }
}

const stream = fromSync(new Greeting('world'));
console.log(textSync(stream)); // 'hello world'
const { fromSync, textSync } = require('node:stream/iter');

class Greeting {
  #name;

  constructor(name) {
    this.#name = name;
  }

  [Symbol.for('Stream.toStreamable')]() {
    return `hello ${this.#name}`;
  }
}

const stream = fromSync(new Greeting('world'));
console.log(textSync(stream)); // 'hello world'
javascript

Modules: node:module API#

Stability: 2 - Stable

The node:module module provides utilities for interacting with the Node.js module system.

It can be accessed using:

import module from 'node:module';
mjs

Class: Module#

module objects in CommonJS modules are instances of this class.

See the CommonJS documentation for the module object.

module.builtinModules#

A list of the names of all modules provided by Node.js.

import { builtinModules } from 'node:module';
console.log(builtinModules.filter((name) => name.startsWith('path')));
// Prints: [ 'path', 'path/posix', 'path/win32' ]
mjs

To test whether a module name corresponds to a builtin module, use module.isBuiltin().

module.createRequire(filename)#

  • filename <string> | <URL> Filename to be used to construct the require function. Must be a file URL object, file URL string, or absolute path string.
  • Returns: <require> Require function
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);

// sibling-module.js is a CommonJS module.
const siblingModule = require('./sibling-module');
mjs

module.findPackageJSON(specifier[, base])#

Stability: 1.1 - Active Development

  • specifier <string> | <URL> The specifier for the module whose package.json to retrieve. When passing a bare specifier, the package.json at the root of the package is returned. When passing a relative specifier or an absolute specifier, the closest parent package.json is returned.
  • base <string> | <URL> The absolute location (file: URL string or FS path) of the containing module. For CJS, use __filename (not __dirname!); for ESM, use import.meta.url. You do not need to pass it if specifier is an absolute specifier.
  • Returns: <string> | <undefined> A path if the package.json is found. When specifier is a package, the package's root package.json; when a relative or unresolved, the closest package.json to the specifier.

Caveat: Do not use this to try to determine module format. There are many things affecting that determination; the type field of package.json is the least definitive (ex file extension supersedes it, and a loader hook supersedes that).

Caveat: This currently leverages only the built-in default resolver; if resolve customization hooks are registered, they will not affect the resolution. This may change in the future.

// /path/to/project/packages/bar/bar.js
import { findPackageJSON } from 'node:module';

findPackageJSON('..', import.meta.url);
// '/path/to/project/package.json'
// Same result when passing an absolute specifier instead:
findPackageJSON(new URL('../', import.meta.url));
findPackageJSON(import.meta.resolve('../'));

findPackageJSON('some-package', import.meta.url);
// '/path/to/project/packages/bar/node_modules/some-package/package.json'
// When passing an absolute specifier, you might get a different result if the
// resolved module is inside a subfolder that has nested `package.json`.
findPackageJSON(import.meta.resolve('some-package'));
// '/path/to/project/packages/bar/node_modules/some-package/some-subfolder/package.json'

findPackageJSON('@foo/qux', import.meta.url);
// '/path/to/project/packages/qux/package.json'
// /path/to/project/packages/bar/bar.js
const { findPackageJSON } = require('node:module');
const { pathToFileURL } = require('node:url');
const path = require('node:path');

findPackageJSON('..', __filename);
// '/path/to/project/package.json'
// Same result when passing an absolute specifier instead:
findPackageJSON(pathToFileURL(path.join(__dirname, '..')));

findPackageJSON('some-package', __filename);
// '/path/to/project/packages/bar/node_modules/some-package/package.json'
// When passing an absolute specifier, you might get a different result if the
// resolved module is inside a subfolder that has nested `package.json`.
findPackageJSON(pathToFileURL(require.resolve('some-package')));
// '/path/to/project/packages/bar/node_modules/some-package/some-subfolder/package.json'

findPackageJSON('@foo/qux', __filename);
// '/path/to/project/packages/qux/package.json'
javascript

module.isBuiltin(moduleName)#

  • moduleName <string> name of the module
  • Returns: <boolean> returns true if the module is builtin else returns false
import { isBuiltin } from 'node:module';
isBuiltin('node:fs'); // true
isBuiltin('fs'); // true
isBuiltin('wss'); // false
mjs

module.register(specifier[, parentURL][, options])#

Stability: 0 - Deprecated: Use module.registerHooks() instead.

  • specifier <string> | <URL> Customization hooks to be registered; this should be the same string that would be passed to import(), except that if it is relative, it is resolved relative to parentURL.
  • parentURL <string> | <URL> If you want to resolve specifier relative to a base URL, such as import.meta.url, you can pass that URL here. Default: 'data:'
  • options <Object>
    • parentURL <string> | <URL> If you want to resolve specifier relative to a base URL, such as import.meta.url, you can pass that URL here. This property is ignored if the parentURL is supplied as the second argument. Default: 'data:'
    • data <any> Any arbitrary, cloneable JavaScript value to pass into the initialize hook.
    • transferList <Object>[] transferable objects to be passed into the initialize hook.

Register a module that exports hooks that customize Node.js module resolution and loading behavior. See Customization hooks.

This feature requires --allow-worker if used with the Permission Model.

module.registerHooks(options)#

Stability: 1.2 - Release candidate

Register hooks that customize Node.js module resolution and loading behavior. See Customization hooks. The returned object can be used to deregister the hooks.

module.stripTypeScriptTypes(code[, options])#

Stability: 1.2 - Release candidate

  • code <string> The code to strip type annotations from.
  • options <Object>
    • mode <string> Default: 'strip'. Possible values are:
      • 'strip' Only strip type annotations without performing the transformation of TypeScript features.
    • sourceUrl <string> Specifies the source url used in the source map.
  • Returns: <string> The code with type annotations stripped.

module.stripTypeScriptTypes() removes type annotations from TypeScript code. It can be used to strip type annotations from TypeScript code before running it with vm.runInContext() or vm.compileFunction().

By default, it will throw an error if the code contains TypeScript features that require transformation, such as enums. See type-stripping for more information.

WARNING: The output of this function should not be considered stable across Node.js versions, due to changes in the TypeScript parser.

import { stripTypeScriptTypes } from 'node:module';
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code);
console.log(strippedCode);
// Prints: const a         = 1;
const { stripTypeScriptTypes } = require('node:module');
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code);
console.log(strippedCode);
// Prints: const a         = 1;
javascript

If sourceUrl is provided, it will be used appended as a comment at the end of the output:

import { stripTypeScriptTypes } from 'node:module';
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' });
console.log(strippedCode);
// Prints: const a         = 1\n\n//# sourceURL=source.ts;
const { stripTypeScriptTypes } = require('node:module');
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' });
console.log(strippedCode);
// Prints: const a         = 1\n\n//# sourceURL=source.ts;
javascript

module.syncBuiltinESMExports()#

The module.syncBuiltinESMExports() method updates all the live bindings for builtin ES Modules to match the properties of the CommonJS exports. It does not add or remove exported names from the ES Modules.

const fs = require('node:fs');
const assert = require('node:assert');
const { syncBuiltinESMExports } = require('node:module');

fs.readFile = newAPI;

delete fs.readFileSync;

function newAPI() {
  // ...
}

fs.newAPI = newAPI;

syncBuiltinESMExports();

import('node:fs').then((esmFS) => {
  // It syncs the existing readFile property with the new value
  assert.strictEqual(esmFS.readFile, newAPI);
  // readFileSync has been deleted from the required fs
  assert.strictEqual('readFileSync' in fs, false);
  // syncBuiltinESMExports() does not remove readFileSync from esmFS
  assert.strictEqual('readFileSync' in esmFS, true);
  // syncBuiltinESMExports() does not add names
  assert.strictEqual(esmFS.newAPI, undefined);
});
js

Module compile cache#

The module compile cache can be enabled either using the module.enableCompileCache() method or the NODE_COMPILE_CACHE=dir environment variable. After it is enabled, whenever Node.js compiles a CommonJS, an ECMAScript Module, or a TypeScript module, it will use on-disk V8 code cache persisted in the specified directory to speed up the compilation. This may slow down the first load of a module graph, but subsequent loads of the same module graph may get a significant speedup if the contents of the modules do not change.

To clean up the generated compile cache on disk, simply remove the cache directory. The cache directory will be recreated the next time the same directory is used for compile cache storage. To avoid filling up the disk with stale cache, it is recommended to use a directory under the os.tmpdir(). If the compile cache is enabled by a call to module.enableCompileCache() without specifying the directory, Node.js will use the NODE_COMPILE_CACHE=dir environment variable if it's set, or defaults to path.join(os.tmpdir(), 'node-compile-cache') otherwise. To locate the compile cache directory used by a running Node.js instance, use module.getCompileCacheDir().

The enabled module compile cache can be disabled by the NODE_DISABLE_COMPILE_CACHE=1 environment variable. This can be useful when the compile cache leads to unexpected or undesired behaviors (e.g. less precise test coverage).

At the moment, when the compile cache is enabled and a module is loaded afresh, the code cache is generated from the compiled code immediately, but will only be written to disk when the Node.js instance is about to exit. This is subject to change. The module.flushCompileCache() method can be used to ensure the accumulated code cache is flushed to disk in case the application wants to spawn other Node.js instances and let them share the cache long before the parent exits.

The compile cache layout on disk is an implementation detail and should not be relied upon. The compile cache generated is typically only reusable in the same version of Node.js, and should be not assumed to be compatible across different versions of Node.js.

Portability of the compile cache#

By default, caches are invalidated when the absolute paths of the modules being cached are changed. To keep the cache working after moving the project directory, enable portable compile cache. This allows previously compiled modules to be reused across different directory locations as long as the layout relative to the cache directory remains the same. This would be done on a best-effort basis. If Node.js cannot compute the location of a module relative to the cache directory, the module will not be cached.

A portable cache is also not split by user: on platforms with uids the cache subdirectory of a non-portable cache is suffixed with the uid of the user who created it, so it is only found by that user, while a portable cache uses the same subdirectory for every user. This lets a cache generated once (for example at build time, then shipped read-only with an application) be read by whoever runs the code; a user who cannot write to the directory still reads it, and a failed write only means the module is compiled again.

There are two ways to enable the portable mode:

  1. Using the portable option in module.enableCompileCache():

    // Non-portable cache (default): cache breaks if project is moved
    module.enableCompileCache({ directory: '/path/to/cache/storage/dir' });
    
    // Portable cache: cache works after the project is moved
    module.enableCompileCache({ directory: '/path/to/cache/storage/dir', portable: true });
    
    js
  2. Setting the environment variable: NODE_COMPILE_CACHE_PORTABLE=1

Read-only compile cache#

A cache that was generated ahead of time, for example at build time to be shipped inside an application package, can be enabled with readOnly: true (or NODE_COMPILE_CACHE_READONLY=1). Node.js then loads whatever entries the directory holds and never writes to it: modules without a usable entry are compiled as usual but not persisted, module.flushCompileCache() is a no-op, and the directory is not created if it is missing.

Limitations of the compile cache#

Currently when using the compile cache with V8 JavaScript code coverage, the coverage being collected by V8 may be less precise in functions that are deserialized from the code cache. It's recommended to turn this off when running tests to generate precise coverage.

Compilation cache generated by one version of Node.js can not be reused by a different version of Node.js. Cache generated by different versions of Node.js will be stored separately if the same base directory is used to persist the cache, so they can co-exist.

module.constants.compileCacheStatus#

The following constants are returned as the status field in the object returned by module.enableCompileCache() to indicate the result of the attempt to enable the module compile cache.

Constant Description
ENABLED Node.js has enabled the compile cache successfully. The directory used to store the compile cache will be returned in the directory field in the returned object.
ALREADY_ENABLED The compile cache has already been enabled before, either by a previous call to module.enableCompileCache(), or by the NODE_COMPILE_CACHE=dir environment variable. The directory used to store the compile cache will be returned in the directory field in the returned object.
FAILED Node.js fails to enable the compile cache. This can be caused by the lack of permission to use the specified directory, or various kinds of file system errors. The detail of the failure will be returned in the message field in the returned object.
DISABLED Node.js cannot enable the compile cache because the environment variable NODE_DISABLE_COMPILE_CACHE=1 has been set.

module.enableCompileCache([options])#

  • options <string> | <Object> Optional. If a string is passed, it is considered to be options.directory.
    • directory <string> Optional. Directory to store the compile cache. If not specified, the directory specified by the NODE_COMPILE_CACHE=dir environment variable will be used if it's set, or path.join(os.tmpdir(), 'node-compile-cache') otherwise.
    • portable <boolean> Optional. If true, enables portable compile cache so that the cache can be reused even if the project directory is moved. This is a best-effort feature. If not specified, it will depend on whether the environment variable NODE_COMPILE_CACHE_PORTABLE=1 is set.
    • readOnly <boolean> Optional. If true, existing cache entries in directory are used but nothing is ever written to it, and the directory is not created when it does not exist (enabling then fails). Meant for caches generated ahead of time and shipped with an application. If not specified, it will depend on whether the environment variable NODE_COMPILE_CACHE_READONLY=1 is set.
  • Returns: <Object>
    • status <integer> One of the module.constants.compileCacheStatus
    • message <string> | <undefined> If Node.js cannot enable the compile cache, this contains the error message. Only set if status is module.constants.compileCacheStatus.FAILED.
    • directory <string> | <undefined> If the compile cache is enabled, this contains the directory where the compile cache is stored. Only set if  status is module.constants.compileCacheStatus.ENABLED or module.constants.compileCacheStatus.ALREADY_ENABLED.

Enable module compile cache in the current Node.js instance.

For general use cases, it's recommended to call module.enableCompileCache() without specifying the options.directory, so that the directory can be overridden by the NODE_COMPILE_CACHE environment variable when necessary.

Since compile cache is supposed to be an optimization that is not mission critical, this method is designed to not throw any exception when the compile cache cannot be enabled. Instead, it will return an object containing an error message in the message field to aid debugging. If compile cache is enabled successfully, the directory field in the returned object contains the path to the directory where the compile cache is stored. The status field in the returned object would be one of the module.constants.compileCacheStatus values to indicate the result of the attempt to enable the module compile cache.

This method only affects the current Node.js instance. To enable it in child worker threads, either call this method in child worker threads too, or set the process.env.NODE_COMPILE_CACHE value to compile cache directory so the behavior can be inherited into the child workers. The directory can be obtained either from the directory field returned by this method, or with module.getCompileCacheDir().

module.flushCompileCache()#

Flush the module compile cache accumulated from modules already loaded in the current Node.js instance to disk. This returns after all the flushing file system operations come to an end, no matter they succeed or not. If there are any errors, this will fail silently, since compile cache misses should not interfere with the actual operation of the application.

module.getCompileCacheDir()#

Customization Hooks#

Node.js currently supports two types of module customization hooks:

  1. module.registerHooks(options): takes synchronous hook functions that are run directly on the thread where the modules are loaded.
  2. module.register(specifier[, parentURL][, options]): takes specifier to a module that exports asynchronous hook functions. The functions are run on a separate loader thread.

The asynchronous hooks incur extra overhead from inter-thread communication, and have several caveats especially when customizing CommonJS modules in the module graph. In most cases, it's recommended to use synchronous hooks via module.registerHooks() for simplicity.

Synchronous customization hooks#

Stability: 1.2 - Release candidate

Registration of synchronous customization hooks#

To register synchronous customization hooks, use module.registerHooks(), which takes synchronous hook functions directly in-line.

// register-hooks.js
import { registerHooks } from 'node:module';
registerHooks({
  resolve(specifier, context, nextResolve) { /* implementation */ },
  load(url, context, nextLoad) { /* implementation */ },
});
// register-hooks.js
const { registerHooks } = require('node:module');
registerHooks({
  resolve(specifier, context, nextResolve) { /* implementation */ },
  load(url, context, nextLoad) { /* implementation */ },
});
javascript
Registering hooks before application code runs with flags#

The hooks can be registered before the application code is run by using the --import or --require flag:

node --import ./register-hooks.js ./my-app.js
node --require ./register-hooks.js ./my-app.js
bash

The specifier passed to --import or --require can also come from a package:

node --import some-package/register ./my-app.js
node --require some-package/register ./my-app.js
bash

Where some-package has an "exports" field defining the /register export to map to a file that calls registerHooks(), like the register-hooks.js examples above.

Using --import or --require ensures that the hooks are registered before any application code is loaded, including the entry point of the application and for any worker threads by default as well.

Registering hooks before application code runs programmatically#

Alternatively, registerHooks() can be called from the entry point.

If the entry point needs to load other modules and the loading process needs to be customized, load them using either require() or dynamic import() after the hooks are registered. Do not use static import statements to load modules that need to be customized in the same module that registers the hooks, because static import statements are evaluated before any code in the importer module is run, including the call to registerHooks(), regardless of where the static import statements appear in the importer module.

import { registerHooks } from 'node:module';

registerHooks({ /* implementation of synchronous hooks */ });

// If loaded using static import, the hooks would not be applied when loading
// my-app.mjs, because statically imported modules are all executed before its
// importer regardless of where the static import appears.
// import './my-app.mjs';

// my-app.mjs must be loaded dynamically to ensure the hooks are applied.
await import('./my-app.mjs');
const { registerHooks } = require('node:module');

registerHooks({ /* implementation of synchronous hooks */ });

import('./my-app.mjs');
// Or, if my-app.mjs does not have top-level await or it's a CommonJS module,
// require() can also be used:
// require('./my-app.mjs');
javascript
Registering hooks before application code runs with a data: URL#

Alternatively, inline JavaScript code can be embedded in data: URLs to register the hooks before the application code runs. For example,

node --import 'data:text/javascript,import {registerHooks} from "node:module"; registerHooks(/* hooks code */);' ./my-app.js
bash
Convention of hooks and chaining#

Hooks are part of a chain, even if that chain consists of only one custom (user-provided) hook and the default hook, which is always present.

Hook functions nest: each one must always return a plain object, and chaining happens as a result of each function calling next<hookName>(), which is a reference to the subsequent loader's hook (in LIFO order).

It's possible to call registerHooks() more than once:

// entrypoint.mjs
import { registerHooks } from 'node:module';

const hook1 = { /* implementation of hooks */ };
const hook2 = { /* implementation of hooks */ };
// hook2 runs before hook1.
registerHooks(hook1);
registerHooks(hook2);
// entrypoint.cjs
const { registerHooks } = require('node:module');

const hook1 = { /* implementation of hooks */ };
const hook2 = { /* implementation of hooks */ };
// hook2 runs before hook1.
registerHooks(hook1);
registerHooks(hook2);
javascript

In this example, the registered hooks will form chains. These chains run last-in, first-out (LIFO). If both hook1 and hook2 define a resolve hook, they will be called like so (note the right-to-left, starting with hook2.resolve, then hook1.resolve, then the Node.js default):

Node.js default resolve ← hook1.resolve ← hook2.resolve

The same applies to all the other hooks.

A hook that returns a value lacking a required property triggers an exception. A hook that returns without calling next<hookName>() and without returning shortCircuit: true also triggers an exception. These errors are to help prevent unintentional breaks in the chain. Return shortCircuit: true from a hook to signal that the chain is intentionally ending at your hook.

If a hook should be applied when loading other hook modules, the other hook modules should be loaded after the hook is registered.

Deregistration of synchronous customization hooks#

The object returned by registerHooks() has a deregister() method that can be used to remove the hooks from the chain. Once deregister() is called, the hooks will no longer be invoked during module resolution or loading.

This is currently only available for synchronous hooks registered via registerHooks(), not for asynchronous hooks registered via module.register().

import { registerHooks } from 'node:module';

const hooks = registerHooks({
  resolve(specifier, context, nextResolve) {
    console.log('resolve hook called for', specifier);
    return nextResolve(specifier, context);
  },
  load(url, context, nextLoad) {
    return nextLoad(url, context);
  },
});

// At this point, the hooks are active and will be called for
// any subsequent import() or require() calls.
await import('./my-module.mjs');

// Later, remove the hooks from the chain.
hooks.deregister();

// Subsequent loads will no longer trigger the hooks.
await import('./another-module.mjs');
const { registerHooks } = require('node:module');

const hooks = registerHooks({
  resolve(specifier, context, nextResolve) {
    console.log('resolve hook called for', specifier);
    return nextResolve(specifier, context);
  },
  load(url, context, nextLoad) {
    return nextLoad(url, context);
  },
});

// At this point, the hooks are active and will be called for
// any subsequent require() calls.
require('./my-module.cjs');

// Later, remove the hooks from the chain.
hooks.deregister();

// Subsequent loads will no longer trigger the hooks.
require('./another-module.cjs');
javascript
Hook functions accepted by module.registerHooks()#

The module.registerHooks() method accepts the following synchronous hook functions.

function resolve(specifier, context, nextResolve) {
  // Take an `import` or `require` specifier and resolve it to a URL.
}

function load(url, context, nextLoad) {
  // Take a resolved URL and return the source code to be evaluated.
}
mjs

Synchronous hooks are run in the same thread and the same realm where the modules are loaded, the code in the hook function can pass values to the modules being referenced directly via global variables or other shared states.

Unlike the asynchronous hooks, the synchronous hooks are not inherited into child worker threads by default, though if the hooks are registered using a file preloaded by --import or --require, child worker threads can inherit the preloaded scripts via process.execArgv inheritance. See the documentation of Worker for details.

Synchronous resolve(specifier, context, nextResolve)#
  • specifier <string>
  • context <Object>
    • conditions <string>[] Export conditions of the relevant package.json
    • importAttributes <Object> An object whose key-value pairs represent the attributes for the module to import
    • parentURL <string> | <undefined> The module importing this one, or undefined if this is the Node.js entry point
  • nextResolve <Function> The subsequent resolve hook in the chain, or the Node.js default resolve hook after the last user-supplied resolve hook
    • specifier <string>
    • context <Object> | <undefined> When omitted, the defaults are provided. When provided, defaults are merged in with preference to the provided properties.
  • Returns: <Object>
    • format <string> | <null> | <undefined> A hint to the load hook (it might be ignored). It can be a module format (such as 'commonjs' or 'module') or an arbitrary value like 'css' or 'yaml'.
    • importAttributes <Object> | <undefined> The import attributes to use when caching the module (optional; if excluded the input will be used)
    • shortCircuit <undefined> | <boolean> A signal that this hook intends to terminate the chain of resolve hooks. Default: false
    • url <string> The absolute URL to which this input resolves

The resolve hook chain is responsible for telling Node.js where to find and how to cache a given import statement or expression, or require call. It can optionally return a format (such as 'module') as a hint to the load hook. If a format is specified, the load hook is ultimately responsible for providing the final format value (and it is free to ignore the hint provided by resolve); if resolve provides a format, a custom load hook is required even if only to pass the value to the Node.js default load hook.

Import type attributes are part of the cache key for saving loaded modules into the internal module cache. The resolve hook is responsible for returning an importAttributes object if the module should be cached with different attributes than were present in the source code.

The conditions property in context is an array of conditions that will be used to match package exports conditions for this resolution request. They can be used for looking up conditional mappings elsewhere or to modify the list when calling the default resolution logic.

The current package exports conditions are always in the context.conditions array passed into the hook. To guarantee default Node.js module specifier resolution behavior when calling defaultResolve, the context.conditions array passed to it must include all elements of the context.conditions array originally passed into the resolve hook.

import { registerHooks } from 'node:module';

function resolve(specifier, context, nextResolve) {
  // When calling `defaultResolve`, the arguments can be modified. For example,
  // to change the specifier or to add applicable export conditions.
  if (specifier.includes('foo')) {
    specifier = specifier.replace('foo', 'bar');
    return nextResolve(specifier, {
      ...context,
      conditions: [...context.conditions, 'another-condition'],
    });
  }

  // The hook can also skip default resolution and provide a custom URL.
  if (specifier === 'special-module') {
    return {
      url: 'file:///path/to/special-module.mjs',
      format: 'module',
      shortCircuit: true,  // This is mandatory if nextResolve() is not called.
    };
  }

  // If no customization is needed, defer to the next hook in the chain which would be the
  // Node.js default resolve if this is the last user-specified loader.
  return nextResolve(specifier);
}

registerHooks({ resolve });
mjs
Synchronous load(url, context, nextLoad)#
  • url <string> The URL returned by the resolve chain
  • context <Object>
    • conditions <string>[] Export conditions of the relevant package.json
    • format <string> | <null> | <undefined> The format optionally supplied by the resolve hook chain. This can be any string value as an input; input values do not need to conform to the list of acceptable return values described below.
    • importAttributes <Object>
  • nextLoad <Function> The subsequent load hook in the chain, or the Node.js default load hook after the last user-supplied load hook
    • url <string>
    • context <Object> | <undefined> When omitted, defaults are provided. When provided, defaults are merged in with preference to the provided properties. In the default nextLoad, if the module pointed to by url does not have explicit module type information, context.format is mandatory.
  • Returns: <Object>

The load hook provides a way to define a custom method for retrieving the source code of a resolved URL. This would allow a loader to potentially avoid reading files from disk. It could also be used to map an unrecognized format to a supported one, for example yaml to module.

import { registerHooks } from 'node:module';
import { Buffer } from 'node:buffer';

function load(url, context, nextLoad) {
  // The hook can skip default loading and provide a custom source code.
  if (url === 'special-module') {
    return {
      source: 'export const special = 42;',
      format: 'module',
      shortCircuit: true,  // This is mandatory if nextLoad() is not called.
    };
  }

  // It's possible to modify the source code loaded by the next - possibly default - step,
  // for example, replacing 'foo' with 'bar' in the source code of the module.
  const result = nextLoad(url, context);
  const source = typeof result.source === 'string' ?
    result.source : Buffer.from(result.source).toString('utf8');
  return {
    source: source.replace(/foo/g, 'bar'),
    ...result,
  };
}

registerHooks({ load });
mjs

In a more advanced scenario, this can also be used to transform an unsupported source to a supported one (see Examples below).

Accepted final formats returned by load#

The final value of format must be one of the following:

format Description Acceptable types for source returned by load
'addon' Load a Node.js addon <null>
'builtin' Load a Node.js builtin module <null>
'commonjs-typescript' Load a Node.js CommonJS module with TypeScript syntax <string> | <ArrayBuffer> | <TypedArray> | <null> | <undefined>
'commonjs' Load a Node.js CommonJS module <string> | <ArrayBuffer> | <TypedArray> | <null> | <undefined>
'json' Load a JSON file <string> | <ArrayBuffer> | <TypedArray>
'module-typescript' Load an ES module with TypeScript syntax <string> | <ArrayBuffer> | <TypedArray>
'module' Load an ES module <string> | <ArrayBuffer> | <TypedArray>
'wasm' Load a WebAssembly module <ArrayBuffer> | <TypedArray>

The value of source is ignored for format 'builtin' because currently it is not possible to replace the value of a Node.js builtin (core) module.

These types all correspond to classes defined in ECMAScript.

If the source value of a text-based format (i.e., 'json', 'module') is not a string, it is converted to a string using util.TextDecoder.

Asynchronous customization hooks#

Stability: 1.1 - Active Development

Caveats of asynchronous customization hooks#

The asynchronous customization hooks have many caveats and it is uncertain if their issues can be resolved. Users are encouraged to use the synchronous customization hooks via module.registerHooks() instead to avoid these caveats.

  • Asynchronous hooks run on a separate thread, so the hook functions cannot directly mutate the global state of the modules being customized. It's typical to use message channels and atomics to pass data between the two or to affect control flows. See Communication with asynchronous module customization hooks.
  • Asynchronous hooks do not affect all require() calls in the module graph.
    • Custom require functions created using module.createRequire() are not affected.
    • If the asynchronous load hook does not override the source for CommonJS modules that go through it, the child modules loaded by those CommonJS modules via built-in require() would not be affected by the asynchronous hooks either.
  • There are several caveats that the asynchronous hooks need to handle when customizing CommonJS modules. See asynchronous resolve hook and asynchronous load hook for details.
  • When require() calls inside CommonJS modules are customized by asynchronous hooks, Node.js may need to load the source code of the CommonJS module multiple times to maintain compatibility with existing CommonJS monkey-patching. If the module code changes between loads, this may lead to unexpected behaviors.
    • As a side effect, if both asynchronous hooks and synchronous hooks are registered and the asynchronous hooks choose to customize the CommonJS module, the synchronous hooks may be invoked multiple times for the require() calls in that CommonJS module.
Registration of asynchronous customization hooks#

Asynchronous customization hooks are registered using module.register() which takes a path or URL to another module that exports the asynchronous hook functions.

Similar to registerHooks(), register() can be called in a module preloaded by --import or --require, or called directly within the entry point.

// Use module.register() to register asynchronous hooks in a dedicated thread.
import { register } from 'node:module';
register('./hooks.mjs', import.meta.url);

// If my-app.mjs is loaded statically here as `import './my-app.mjs'`, since ESM
// dependencies are evaluated before the module that imports them,
// it's loaded _before_ the hooks are registered above and won't be affected.
// To ensure the hooks are applied, dynamic import() must be used to load ESM
// after the hooks are registered.
import('./my-app.mjs');
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
// Use module.register() to register asynchronous hooks in a dedicated thread.
register('./hooks.mjs', pathToFileURL(__filename));

import('./my-app.mjs');
javascript

In hooks.mjs:

// hooks.mjs
export async function resolve(specifier, context, nextResolve) {
  /* implementation */
}
export async function load(url, context, nextLoad) {
  /* implementation */
}
mjs

Unlike synchronous hooks, the asynchronous hooks would not run for these modules loaded in the file that calls register():

// register-hooks.js
import { register, createRequire } from 'node:module';
register('./hooks.mjs', import.meta.url);

// Asynchronous hooks does not affect modules loaded via custom require()
// functions created by module.createRequire().
const userRequire = createRequire(import.meta.filename);
userRequire('./my-app-2.cjs');  // Hooks won't affect this
// register-hooks.js
const { register, createRequire } = require('node:module');
const { pathToFileURL } = require('node:url');
register('./hooks.mjs', pathToFileURL(__filename));

// Asynchronous hooks does not affect modules loaded via built-in require()
// in the module calling `register()`
require('./my-app-2.cjs');  // Hooks won't affect this
// .. or custom require() functions created by module.createRequire().
const userRequire = createRequire(__filename);
userRequire('./my-app-3.cjs');  // Hooks won't affect this
javascript

Asynchronous hooks can also be registered using a data: URL with the --import flag:

node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("my-instrumentation", pathToFileURL("./"));' ./my-app.js
bash
Chaining of asynchronous customization hooks#

Chaining of register() work similarly to registerHooks(). If synchronous and asynchronous hooks are mixed, the synchronous hooks are always run first before the asynchronous hooks start running, that is, in the last synchronous hook being run, its next hook includes invocation of the asynchronous hooks.

// entrypoint.mjs
import { register } from 'node:module';

register('./foo.mjs', import.meta.url);
register('./bar.mjs', import.meta.url);
await import('./my-app.mjs');
// entrypoint.cjs
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');

const parentURL = pathToFileURL(__filename);
register('./foo.mjs', parentURL);
register('./bar.mjs', parentURL);
import('./my-app.mjs');
javascript

If foo.mjs and bar.mjs define a resolve hook, they will be called like so (note the right-to-left, starting with ./bar.mjs, then ./foo.mjs, then the Node.js default):

Node.js default ← ./foo.mjs ← ./bar.mjs

When using the asynchronous hooks, the registered hooks also affect subsequent register calls, which takes care of loading hook modules. In the example above, bar.mjs will be resolved and loaded via the hooks registered by foo.mjs (because foo's hooks will have already been added to the chain). This allows for things like writing hooks in non-JavaScript languages, so long as earlier registered hooks transpile into JavaScript.

The register() method cannot be called from the thread running the hook module that exports the asynchronous hooks or its dependencies.

Communication with asynchronous module customization hooks#

Asynchronous hooks run on a dedicated thread, separate from the main thread that runs application code. This means mutating global variables won't affect the other thread(s), and message channels must be used to communicate between the threads.

The register method can be used to pass data to an initialize hook. The data passed to the hook may include transferable objects like ports.

import { register } from 'node:module';
import { MessageChannel } from 'node:worker_threads';

// This example demonstrates how a message channel can be used to
// communicate with the hooks, by sending `port2` to the hooks.
const { port1, port2 } = new MessageChannel();

port1.on('message', (msg) => {
  console.log(msg);
});
port1.unref();

register('./my-hooks.mjs', {
  parentURL: import.meta.url,
  data: { number: 1, port: port2 },
  transferList: [port2],
});
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
const { MessageChannel } = require('node:worker_threads');

// This example showcases how a message channel can be used to
// communicate with the hooks, by sending `port2` to the hooks.
const { port1, port2 } = new MessageChannel();

port1.on('message', (msg) => {
  console.log(msg);
});
port1.unref();

register('./my-hooks.mjs', {
  parentURL: pathToFileURL(__filename),
  data: { number: 1, port: port2 },
  transferList: [port2],
});
javascript
Asynchronous hooks accepted by module.register()#

The register method can be used to register a module that exports a set of hooks. The hooks are functions that are called by Node.js to customize the module resolution and loading process. The exported functions must have specific names and signatures, and they must be exported as named exports.

export async function initialize({ number, port }) {
  // Receives data from `register`.
}

export async function resolve(specifier, context, nextResolve) {
  // Take an `import` or `require` specifier and resolve it to a URL.
}

export async function load(url, context, nextLoad) {
  // Take a resolved URL and return the source code to be evaluated.
}
mjs

Asynchronous hooks are run in a separate thread, isolated from the main thread where application code runs. That means it is a different realm. The hooks thread may be terminated by the main thread at any time, so do not depend on asynchronous operations (like console.log) to complete. They are inherited into child workers by default.

initialize()#
  • data <any> The data from register(loader, import.meta.url, { data }).

The initialize hook is only accepted by register. registerHooks() does not support nor need it since initialization done for synchronous hooks can be run directly before the call to registerHooks().

The initialize hook provides a way to define a custom function that runs in the hooks thread when the hooks module is initialized. Initialization happens when the hooks module is registered via register.

This hook can receive data from a register invocation, including ports and other transferable objects. The return value of initialize can be a <Promise>, in which case it will be awaited before the main application thread execution resumes.

Module customization code:

// path-to-my-hooks.js

export async function initialize({ number, port }) {
  port.postMessage(`increment: ${number + 1}`);
}
mjs

Caller code:

import assert from 'node:assert';
import { register } from 'node:module';
import { MessageChannel } from 'node:worker_threads';

// This example showcases how a message channel can be used to communicate
// between the main (application) thread and the hooks running on the hooks
// thread, by sending `port2` to the `initialize` hook.
const { port1, port2 } = new MessageChannel();

port1.on('message', (msg) => {
  assert.strictEqual(msg, 'increment: 2');
});
port1.unref();

register('./path-to-my-hooks.js', {
  parentURL: import.meta.url,
  data: { number: 1, port: port2 },
  transferList: [port2],
});
const assert = require('node:assert');
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
const { MessageChannel } = require('node:worker_threads');

// This example showcases how a message channel can be used to communicate
// between the main (application) thread and the hooks running on the hooks
// thread, by sending `port2` to the `initialize` hook.
const { port1, port2 } = new MessageChannel();

port1.on('message', (msg) => {
  assert.strictEqual(msg, 'increment: 2');
});
port1.unref();

register('./path-to-my-hooks.js', {
  parentURL: pathToFileURL(__filename),
  data: { number: 1, port: port2 },
  transferList: [port2],
});
javascript
Asynchronous resolve(specifier, context, nextResolve)#
  • specifier <string>
  • context <Object>
    • conditions <string>[] Export conditions of the relevant package.json
    • importAttributes <Object> An object whose key-value pairs represent the attributes for the module to import
    • parentURL <string> | <undefined> The module importing this one, or undefined if this is the Node.js entry point
  • nextResolve <Function> The subsequent resolve hook in the chain, or the Node.js default resolve hook after the last user-supplied resolve hook
    • specifier <string>
    • context <Object> | <undefined> When omitted, the defaults are provided. When provided, defaults are merged in with preference to the provided properties.
  • Returns: <Object> | <Promise> The asynchronous version takes either an object containing the following properties, or a Promise that will resolve to such an object.
    • format <string> | <null> | <undefined> A hint to the load hook (it might be ignored). It can be a module format (such as 'commonjs' or 'module') or an arbitrary value like 'css' or 'yaml'.
    • importAttributes <Object> | <undefined> The import attributes to use when caching the module (optional; if excluded the input will be used)
    • shortCircuit <undefined> | <boolean> A signal that this hook intends to terminate the chain of resolve hooks. Default: false
    • url <string> The absolute URL to which this input resolves

The asynchronous version works similarly to the synchronous version, only that the nextResolve function returns a Promise, and the resolve hook itself can return a Promise.

Warning In the case of the asynchronous version, despite support for returning promises and async functions, calls to resolve may still block the main thread which can impact performance.

Warning The resolve hook invoked for require() calls inside CommonJS modules customized by asynchronous hooks does not receive the original specifier passed to require(). Instead, it receives a URL already fully resolved using the default CommonJS resolution.

Warning In the CommonJS modules that are customized by the asynchronous customization hooks, require.resolve() and require() will use "import" export condition instead of "require", which may cause unexpected behaviors when loading dual packages.

export async function resolve(specifier, context, nextResolve) {
  // When calling `defaultResolve`, the arguments can be modified. For example,
  // to change the specifier or add conditions.
  if (specifier.includes('foo')) {
    specifier = specifier.replace('foo', 'bar');
    return nextResolve(specifier, {
      ...context,
      conditions: [...context.conditions, 'another-condition'],
    });
  }

  // The hook can also skips default resolution and provide a custom URL.
  if (specifier === 'special-module') {
    return {
      url: 'file:///path/to/special-module.mjs',
      format: 'module',
      shortCircuit: true,  // This is mandatory if not calling nextResolve().
    };
  }

  // If no customization is needed, defer to the next hook in the chain which would be the
  // Node.js default resolve if this is the last user-specified loader.
  return nextResolve(specifier);
}
mjs
Asynchronous load(url, context, nextLoad)#
  • url <string> The URL returned by the resolve chain
  • context <Object>
    • conditions <string>[] Export conditions of the relevant package.json
    • format <string> | <null> | <undefined> The format optionally supplied by the resolve hook chain. This can be any string value as an input; input values do not need to conform to the list of acceptable return values described below.
    • importAttributes <Object>
  • nextLoad <Function> The subsequent load hook in the chain, or the Node.js default load hook after the last user-supplied load hook
    • url <string>
    • context <Object> | <undefined> When omitted, defaults are provided. When provided, defaults are merged in with preference to the provided properties. In the default nextLoad, if the module pointed to by url does not have explicit module type information, context.format is mandatory.
  • Returns: <Promise> The asynchronous version takes either an object containing the following properties, or a Promise that will resolve to such an object.

Warning: The asynchronous load hook and namespaced exports from CommonJS modules are incompatible. Attempting to use them together will result in an empty object from the import. This may be addressed in the future. This does not apply to the synchronous load hook, in which case exports can be used as usual.

The asynchronous version works similarly to the synchronous version, though when using the asynchronous load hook, omitting vs providing a source for 'commonjs' has very different effects:

  • When a source is provided, all require calls from this module will be processed by the ESM loader with registered resolve and load hooks; all require.resolve calls from this module will be processed by the ESM loader with registered resolve hooks; only a subset of the CommonJS API will be available (e.g. no require.extensions, no require.cache, no require.resolve.paths) and monkey-patching on the CommonJS module loader will not apply.
  • If source is undefined or null, it will be handled by the CommonJS module loader and require/require.resolve calls will not go through the registered hooks. This behavior for nullish source is temporary — in the future, nullish source will not be supported.

These caveats do not apply to the synchronous load hook, in which case the complete set of CommonJS APIs available to the customized CommonJS modules, and require/require.resolve always go through the registered hooks.

The Node.js internal asynchronous load implementation, which is the value of next for the last hook in the load chain, returns null for source when format is 'commonjs' for backward compatibility. Here is an example hook that would opt-in to using the non-default behavior:

import { readFile } from 'node:fs/promises';

// Asynchronous version accepted by module.register(). This fix is not needed
// for the synchronous version accepted by module.registerHooks().
export async function load(url, context, nextLoad) {
  const result = await nextLoad(url, context);
  if (result.format === 'commonjs') {
    result.source ??= await readFile(new URL(result.responseURL ?? url));
  }
  return result;
}
mjs

This doesn't apply to the synchronous load hook either, in which case the source returned contains source code loaded by the next hook, regardless of module format.

Examples#

The various module customization hooks can be used together to accomplish wide-ranging customizations of the Node.js code loading and evaluation behaviors.

Import from HTTPS#

The hook below registers hooks to enable rudimentary support for such specifiers. While this may seem like a significant improvement to Node.js core functionality, there are substantial downsides to actually using these hooks: performance is much slower than loading files from disk, there is no caching, and there is no security.

// https-hooks.mjs
import { get } from 'node:https';

export function load(url, context, nextLoad) {
  // For JavaScript to be loaded over the network, we need to fetch and
  // return it.
  if (url.startsWith('https://')) {
    return new Promise((resolve, reject) => {
      get(url, (res) => {
        let data = '';
        res.setEncoding('utf8');
        res.on('data', (chunk) => data += chunk);
        res.on('end', () => resolve({
          // This example assumes all network-provided JavaScript is ES module
          // code.
          format: 'module',
          shortCircuit: true,
          source: data,
        }));
      }).on('error', (err) => reject(err));
    });
  }

  // Let Node.js handle all other URLs.
  return nextLoad(url);
}
mjs
// main.mjs
import { VERSION } from 'https://coffeescript.org/browser-compiler-modern/coffeescript.js';

console.log(VERSION);
mjs

With the preceding hooks module, running node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./https-hooks.mjs"));' ./main.mjs prints the current version of CoffeeScript per the module at the URL in main.mjs.

Transpilation#

Sources that are in formats Node.js doesn't understand can be converted into JavaScript using the load hook.

This is less performant than transpiling source files before running Node.js; transpiler hooks should only be used for development and testing purposes.

Asynchronous version#
// coffeescript-hooks.mjs
import { readFile } from 'node:fs/promises';
import { findPackageJSON } from 'node:module';
import coffeescript from 'coffeescript';

const extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/;

export async function load(url, context, nextLoad) {
  if (extensionsRegex.test(url)) {
    // CoffeeScript files can be either CommonJS or ES modules. Use a custom format
    // to tell Node.js not to detect its module type.
    const { source: rawSource } = await nextLoad(url, { ...context, format: 'coffee' });
    // This hook converts CoffeeScript source code into JavaScript source code
    // for all imported CoffeeScript files.
    const transformedSource = coffeescript.compile(rawSource.toString(), url);

    // To determine how Node.js would interpret the transpilation result,
    // search up the file system for the nearest parent package.json file
    // and read its "type" field.
    return {
      format: await getPackageType(url),
      shortCircuit: true,
      source: transformedSource,
    };
  }

  // Let Node.js handle all other URLs.
  return nextLoad(url, context);
}

async function getPackageType(url) {
  // `url` is only a file path during the first iteration when passed the
  // resolved url from the load() hook
  // an actual file path from load() will contain a file extension as it's
  // required by the spec
  // this simple truthy check for whether `url` contains a file extension will
  // work for most projects but does not cover some edge-cases (such as
  // extensionless files or a url ending in a trailing space)
  const pJson = findPackageJSON(url);

  return readFile(pJson, 'utf8')
    .then(JSON.parse)
    .then((json) => json?.type)
    .catch(() => undefined);
}
mjs
Synchronous version#
// coffeescript-sync-hooks.mjs
import { readFileSync } from 'node:fs';
import { registerHooks, findPackageJSON } from 'node:module';
import coffeescript from 'coffeescript';

const extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/;

function load(url, context, nextLoad) {
  if (extensionsRegex.test(url)) {
    const { source: rawSource } = nextLoad(url, { ...context, format: 'coffee' });
    const transformedSource = coffeescript.compile(rawSource.toString(), url);

    return {
      format: getPackageType(url),
      shortCircuit: true,
      source: transformedSource,
    };
  }

  return nextLoad(url, context);
}

function getPackageType(url) {
  const pJson = findPackageJSON(url);
  if (!pJson) {
    return undefined;
  }
  try {
    const file = readFileSync(pJson, 'utf-8');
    return JSON.parse(file)?.type;
  } catch {
    return undefined;
  }
}

registerHooks({ load });
mjs
Running hooks#
# main.coffee
import { scream } from './scream.coffee'
console.log scream 'hello, world'

import { version } from 'node:process'
console.log "Brought to you by Node.js version #{version}"
coffee
# scream.coffee
export scream = (str) -> str.toUpperCase()
coffee

For the sake of running the example, add a package.json file containing the module type of the CoffeeScript files.

{
  "type": "module"
}
json

This is only for running the example. In real world loaders, getPackageType() must be able to return an format known to Node.js even in the absence of an explicit type in a package.json, or otherwise the nextLoad call would throw ERR_UNKNOWN_FILE_EXTENSION (if undefined) or ERR_UNKNOWN_MODULE_FORMAT (if it's not a known format listed in the load hook documentation).

With the preceding hooks modules, running node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./coffeescript-hooks.mjs"));' ./main.coffee or node --import ./coffeescript-sync-hooks.mjs ./main.coffee causes main.coffee to be turned into JavaScript after its source code is loaded from disk but before Node.js executes it; and so on for any .coffee, .litcoffee or .coffee.md files referenced via import statements of any loaded file.

Import maps#

The previous two examples defined load hooks. This is an example of a resolve hook. This hooks module reads an import-map.json file that defines which specifiers to override to other URLs (this is a very simplistic implementation of a small subset of the "import maps" specification).

Asynchronous version#
// import-map-hooks.js
import fs from 'node:fs/promises';

const { imports } = JSON.parse(await fs.readFile('import-map.json'));

export async function resolve(specifier, context, nextResolve) {
  if (Object.hasOwn(imports, specifier)) {
    return nextResolve(imports[specifier], context);
  }

  return nextResolve(specifier, context);
}
mjs
Synchronous version#
// import-map-sync-hooks.js
import fs from 'node:fs/promises';
import module from 'node:module';

const { imports } = JSON.parse(fs.readFileSync('import-map.json', 'utf-8'));

function resolve(specifier, context, nextResolve) {
  if (Object.hasOwn(imports, specifier)) {
    return nextResolve(imports[specifier], context);
  }

  return nextResolve(specifier, context);
}

module.registerHooks({ resolve });
mjs
Using the hooks#

With these files:

// main.js
import 'a-module';
mjs
// import-map.json
{
  "imports": {
    "a-module": "./some-module.js"
  }
}
json
// some-module.js
console.log('some module!');
mjs

Running node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./import-map-hooks.js"));' main.js or node --import ./import-map-sync-hooks.js main.js should print some module!.

Source Map Support#

Stability: 1 - Experimental

Node.js supports TC39 ECMA-426 Source Map format (it was called Source map revision 3 format).

The APIs in this section are helpers for interacting with the source map cache. This cache is populated when source map parsing is enabled and source map include directives are found in a modules' footer.

To enable source map parsing, Node.js must be run with the flag --enable-source-maps, or with code coverage enabled by setting NODE_V8_COVERAGE=dir, or be enabled programmatically via module.setSourceMapsSupport().

// module.mjs
// In an ECMAScript module
import { findSourceMap, SourceMap } from 'node:module';
// module.cjs
// In a CommonJS module
const { findSourceMap, SourceMap } = require('node:module');
javascript

module.getSourceMapsSupport()#

  • Returns: <Object>
    • enabled <boolean> If the source maps support is enabled
    • nodeModules <boolean> If the support is enabled for files in node_modules.
    • generatedCode <boolean> If the support is enabled for generated code from eval or new Function.

This method returns whether the Source Map v3 support for stack traces is enabled.

module.findSourceMap(path)#

path is the resolved path for the file for which a corresponding source map should be fetched.

module.setSourceMapsSupport(enabled[, options])#

  • enabled <boolean> Enable the source map support.
  • options <Object> Optional
    • nodeModules <boolean> If enabling the support for files in node_modules. Default: false.
    • generatedCode <boolean> If enabling the support for generated code from eval or new Function. Default: false.

This function enables or disables the Source Map v3 support for stack traces.

It provides same features as launching Node.js process with commandline options --enable-source-maps, with additional options to alter the support for files in node_modules or generated codes.

Only source maps in JavaScript files that are loaded after source maps has been enabled will be parsed and loaded. Preferably, use the commandline options --enable-source-maps to avoid losing track of source maps of modules loaded before this API call.

Class: module.SourceMap#

new SourceMap(payload[, { lineLengths }])#

Creates a new sourceMap instance.

payload is an object with keys matching the Source map format:

lineLengths is an optional array of the length of each line in the generated code.

sourceMap.payload#

Getter for the payload used to construct the SourceMap instance.

sourceMap.findEntry(lineOffset, columnOffset)#
  • lineOffset <number> The zero-indexed line number offset in the generated source
  • columnOffset <number> The zero-indexed column number offset in the generated source
  • Returns: <Object>

Given a line offset and column offset in the generated source file, returns an object representing the SourceMap range in the original file if found, or an empty object if not.

The object returned contains the following keys:

  • generatedLine <number> The line offset of the start of the range in the generated source
  • generatedColumn <number> The column offset of start of the range in the generated source
  • originalSource <string> The file name of the original source, as reported in the SourceMap
  • originalLine <number> The line offset of the start of the range in the original source
  • originalColumn <number> The column offset of start of the range in the original source
  • name <string>

The returned value represents the raw range as it appears in the SourceMap, based on zero-indexed offsets, not 1-indexed line and column numbers as they appear in Error messages and CallSite objects.

To get the corresponding 1-indexed line and column numbers from a lineNumber and columnNumber as they are reported by Error stacks and CallSite objects, use sourceMap.findOrigin(lineNumber, columnNumber)

sourceMap.findOrigin(lineNumber, columnNumber)#
  • lineNumber <number> The 1-indexed line number of the call site in the generated source
  • columnNumber <number> The 1-indexed column number of the call site in the generated source
  • Returns: <Object>

Given a 1-indexed lineNumber and columnNumber from a call site in the generated source, find the corresponding call site location in the original source.

If the lineNumber and columnNumber provided are not found in any source map, then an empty object is returned. Otherwise, the returned object contains the following keys:

  • name <string> | <undefined> The name of the range in the source map, if one was provided
  • fileName <string> The file name of the original source, as reported in the SourceMap
  • lineNumber <number> The 1-indexed lineNumber of the corresponding call site in the original source
  • columnNumber <number> The 1-indexed columnNumber of the corresponding call site in the original source

Modules: CommonJS modules#

Stability: 2 - Stable

CommonJS modules are the original way to package JavaScript code for Node.js. Node.js also supports the ECMAScript modules standard used by browsers and other JavaScript runtimes.

In Node.js, each file is treated as a separate module. For example, consider a file named foo.js:

const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);
js

On the first line, foo.js loads the module circle.js that is in the same directory as foo.js.

Here are the contents of circle.js:

const { PI } = Math;

exports.area = (r) => PI * r ** 2;

exports.circumference = (r) => 2 * PI * r;
js

The module circle.js has exported the functions area() and circumference(). Functions and objects are added to the root of a module by specifying additional properties on the special exports object.

Variables local to the module will be private, because the module is wrapped in a function by Node.js (see module wrapper). In this example, the variable PI is private to circle.js.

The module.exports property can be assigned a new value (such as a function or object).

In the following code, bar.js makes use of the square module, which exports a Square class:

const Square = require('./square.js');
const mySquare = new Square(2);
console.log(`The area of mySquare is ${mySquare.area()}`);
js

The square module is defined in square.js:

// Assigning to exports will not modify module, must use module.exports
module.exports = class Square {
  constructor(width) {
    this.width = width;
  }

  area() {
    return this.width ** 2;
  }
};
js

The CommonJS module system is implemented in the module core module.

Enabling#

Node.js has two module systems: CommonJS modules and ECMAScript modules.

By default, Node.js will treat the following as CommonJS modules:

  • Files with a .cjs extension.

  • Files with a .js extension or without an extension, when the nearest parent package.json file contains a top-level field "type" with a value of "commonjs".

  • Files with a .js extension or without an extension, when the nearest parent package.json file doesn't contain a top-level field "type" or there is no package.json in any parent folder; unless the file contains syntax that errors unless it is evaluated as an ES module. Package authors should include the "type" field, even in packages where all sources are CommonJS. Being explicit about the type of the package will make things easier for build tools and loaders to determine how the files in the package should be interpreted.

  • Files with an extension that is not .mjs, .cjs, .json, .node, or .js, when the nearest parent package.json file contains a top-level field "type" with a value of "module".

See Determining module system for more details.

Calling require() always use the CommonJS module loader. Calling import() always use the ECMAScript module loader.

Accessing the main module#

When a file is run directly from Node.js, require.main is set to its module. That means that it is possible to determine whether a file has been run directly by testing require.main === module.

For a file foo.js, this will be true if run via node foo.js, but false if run by require('./foo').

When the entry point is not a CommonJS module, require.main is undefined, and the main module is out of reach.

Package manager tips#

The semantics of the Node.js require() function were designed to be general enough to support reasonable directory structures. Package manager programs such as dpkg, rpm, and npm will hopefully find it possible to build native packages from Node.js modules without modification.

In the following, we give a suggested directory structure that could work:

Let's say that we wanted to have the folder at /usr/lib/node/<some-package>/<some-version> hold the contents of a specific version of a package.

Packages can depend on one another. In order to install package foo, it may be necessary to install a specific version of package bar. The bar package may itself have dependencies, and in some cases, these may even collide or form cyclic dependencies.

Because Node.js looks up the realpath of any modules it loads (that is, it resolves symlinks) and then looks for their dependencies in node_modules folders, this situation can be resolved with the following architecture:

  • /usr/lib/node/foo/1.2.3/: Contents of the foo package, version 1.2.3.
  • /usr/lib/node/bar/4.3.2/: Contents of the bar package that foo depends on.
  • /usr/lib/node/foo/1.2.3/node_modules/bar: Symbolic link to /usr/lib/node/bar/4.3.2/.
  • /usr/lib/node/bar/4.3.2/node_modules/*: Symbolic links to the packages that bar depends on.

Thus, even if a cycle is encountered, or if there are dependency conflicts, every module will be able to get a version of its dependency that it can use.

When the code in the foo package does require('bar'), it will get the version that is symlinked into /usr/lib/node/foo/1.2.3/node_modules/bar. Then, when the code in the bar package calls require('quux'), it'll get the version that is symlinked into /usr/lib/node/bar/4.3.2/node_modules/quux.

Furthermore, to make the module lookup process even more optimal, rather than putting packages directly in /usr/lib/node, we could put them in /usr/lib/node_modules/<name>/<version>. Then Node.js will not bother looking for missing dependencies in /usr/node_modules or /node_modules.

In order to make modules available to the Node.js REPL, it might be useful to also add the /usr/lib/node_modules folder to the $NODE_PATH environment variable. Since the module lookups using node_modules folders are all relative, and based on the real path of the files making the calls to require(), the packages themselves can be anywhere.

Loading ECMAScript modules using require()#

The .mjs extension is reserved for ECMAScript Modules. See Determining module system section for more info regarding which files are parsed as ECMAScript modules.

require() only supports loading ECMAScript modules that meet the following requirements:

  • The module is fully synchronous (contains no top-level await); and
  • One of these conditions are met:
    1. The file has a .mjs extension.
    2. The file has a .js extension, and the closest package.json contains "type": "module"
    3. The file has a .js extension, the closest package.json does not contain "type": "commonjs", and the module contains ES module syntax.

If the ES Module being loaded meets the requirements, require() can load it and return the module namespace object. In this case it is similar to dynamic import() but is run synchronously and returns the name space object directly.

With the following ES Modules:

// distance.mjs
export function distance(a, b) { return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); }
mjs
// point.mjs
export default class Point {
  constructor(x, y) { this.x = x; this.y = y; }
}
mjs

A CommonJS module can load them with require():

const distance = require('./distance.mjs');
console.log(distance);
// [Module: null prototype] {
//   distance: [Function: distance]
// }

const point = require('./point.mjs');
console.log(point);
// [Module: null prototype] {
//   default: [class Point],
//   __esModule: true,
// }
cjs

For interoperability with existing tools that convert ES Modules into CommonJS, which could then load real ES Modules through require(), the returned namespace would contain a __esModule: true property if it has a default export so that consuming code generated by tools can recognize the default exports in real ES Modules. If the namespace already defines __esModule, this would not be added. This property is experimental and can change in the future. It should only be used by tools converting ES modules into CommonJS modules, following existing ecosystem conventions. Code authored directly in CommonJS should avoid depending on it.

The result returned by require() is the module namespace object, which places the default export in the .default property, similar to the results returned by import(). To customize what should be returned by require(esm) directly, the ES Module can export the desired value using the string name "module.exports".

// point.mjs
export default class Point {
  constructor(x, y) { this.x = x; this.y = y; }
}

// `distance` is lost to CommonJS consumers of this module, unless it's
// added to `Point` as a static property.
export function distance(a, b) { return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); }
export { Point as 'module.exports' };
const Point = require('./point.mjs');
console.log(Point); // [class Point]

// Named exports are lost when 'module.exports' is used
const { distance } = require('./point.mjs');
console.log(distance); // undefined
javascript

Notice in the example above, when the module.exports export name is used, named exports will be lost to CommonJS consumers. To allow CommonJS consumers to continue accessing named exports, the module can make sure that the default export is an object with the named exports attached to it as properties. For example with the example above, distance can be attached to the default export, the Point class, as a static method.

export function distance(a, b) { return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); }

export default class Point {
  constructor(x, y) { this.x = x; this.y = y; }
  static distance = distance;
}

export { Point as 'module.exports' };
const Point = require('./point.mjs');
console.log(Point); // [class Point]

const { distance } = require('./point.mjs');
console.log(distance); // [Function: distance]
javascript

If the module being require()'d contains top-level await, or the module graph it imports contains top-level await, ERR_REQUIRE_ASYNC_MODULE will be thrown. In this case, users should load the asynchronous module using import().

If --experimental-print-required-tla is enabled and the error is uncaught, Node.js will try to locate the top-level awaits in the require()'d module graph and print the locations in the stderr.

If support for loading ES modules using require() results in unexpected breakage, it can be disabled using --no-require-module. To print where this feature is used, use --trace-require-module.

This feature can be detected by checking if process.features.require_module is true.

All together#

To get the exact filename that will be loaded when require() is called, use the require.resolve() function.

Putting together all of the above, here is the high-level algorithm in pseudocode of what require() does:

require(X) from module at path Y
1. If X is a core module,
   a. return the core module
   b. STOP
2. If X begins with '/'
   a. set Y to the file system root
3. If X is equal to '.', or X begins with './', '/' or '../'
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
   c. THROW "not found"
4. If X begins with '#'
   a. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
5. LOAD_PACKAGE_SELF(X, dirname(Y))
6. If a package map PACKAGE_MAP exists,
   a. Find the package ID for the package owning Y
        1. Let PARENT_PACKAGE_ID be FIND_PACKAGE_ID(dirname(Y), PACKAGE_MAP)
   b. LOAD_PACKAGE_MAP(X, PARENT_PACKAGE_ID, PACKAGE_MAP)
7. LOAD_NODE_MODULES(X, dirname(Y))
8. THROW "not found"

MAYBE_DETECT_AND_LOAD(X)
1. If X parses as a CommonJS module, load X as a CommonJS module. STOP.
2. Else, if the source code of X can be parsed as ECMAScript module using
  DETECT_MODULE_SYNTAX defined in the ESM resolver,
  a. Load X as an ECMAScript module. STOP.
3. THROW the SyntaxError from attempting to parse X as CommonJS in 1. STOP.

LOAD_AS_FILE(X)
1. If X is a file, load X as its file extension format. STOP
2. If X.js is a file,
    a. Find the closest package scope SCOPE to X.
    b. If no scope was found
      1. MAYBE_DETECT_AND_LOAD(X.js)
    c. If the SCOPE/package.json contains "type" field,
      1. If the "type" field is "module", load X.js as an ECMAScript module. STOP.
      2. If the "type" field is "commonjs", load X.js as a CommonJS module. STOP.
    d. MAYBE_DETECT_AND_LOAD(X.js)
3. If X.json is a file, load X.json to a JavaScript Object. STOP
4. If X.node is a file, load X.node as binary addon. STOP

LOAD_INDEX(X)
1. If X/index.js is a file
    a. Find the closest package scope SCOPE to X.
    b. If no scope was found, load X/index.js as a CommonJS module. STOP.
    c. If the SCOPE/package.json contains "type" field,
      1. If the "type" field is "module", load X/index.js as an ECMAScript module. STOP.
      2. Else, load X/index.js as a CommonJS module. STOP.
2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
3. If X/index.node is a file, load X/index.node as binary addon. STOP

LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
   a. Parse X/package.json, and look for "main" field.
   b. If "main" is a falsy value, GOTO 2.
   c. let M = X + (json main field)
   d. LOAD_AS_FILE(M)
   e. LOAD_INDEX(M)
   f. LOAD_INDEX(X) DEPRECATED
   g. THROW "not found"
2. LOAD_INDEX(X)

LOAD_NODE_MODULES(X, START)
1. Try to interpret X as a combination of NAME and SUBPATH where the name
   may have a @scope/ prefix and the subpath begins with a slash (`/`).
2. let DIRS = NODE_MODULES_PATHS(START)
3. for each DIR in DIRS:
   a. LOAD_PACKAGE_EXPORTS(SUBPATH, DIR/NAME)
   b. LOAD_AS_FILE(DIR/X)
   c. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
   a. if PARTS[I] = "node_modules", GOTO d.
   b. DIR = path join(PARTS[0 .. I] + "node_modules")
   c. DIRS = DIRS + DIR
   d. let I = I - 1
5. return DIRS + GLOBAL_FOLDERS

FIND_PACKAGE_ID(PATH, PACKAGE_MAP)
1. Find the PACKAGE_ID for the entry whose "path" is a parent directory of PATH
2. If multiple entries are found, THROW "ambiguous resolution"
3. If no entry was found, THROW "external file".
4. return PACKAGE_ID

LOAD_PACKAGE_MAP(X, PARENT_PACKAGE_ID, PACKAGE_MAP)
1. Try to interpret X as a combination of NAME and SUBPATH where the name
   may have a @scope/ prefix and the subpath begins with a slash (`/`).
2. Find the package map entry for key PARENT_PACKAGE_ID
3. Look up NAME in the entry's "dependencies" map.
4. If NAME is not found, THROW "not found".
5. Let TARGET be PACKAGE_MAP.packages[dependencies[name]]
6. Let PACKAGE_PATH be the resolved path of TARGET.
7. LOAD_PACKAGE_EXPORTS(SUBPATH, PACKAGE_PATH)
8. LOAD_AS_FILE(PACKAGE_PATH/SUBPATH)
9. LOAD_AS_DIRECTORY(PACKAGE_PATH/SUBPATH)
10. THROW "not found"

LOAD_PACKAGE_IMPORTS(X, DIR)
1. Find the closest package scope SCOPE to DIR.
2. If no scope was found, return.
3. If the SCOPE/package.json "imports" is null or undefined, return.
4. If `--no-require-module` is not enabled
  a. let CONDITIONS = ["node", "require", "module-sync"]
  b. Else, let CONDITIONS = ["node", "require"]
5. let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE),
  CONDITIONS) defined in the ESM resolver.
6. RESOLVE_ESM_MATCH(MATCH).

LOAD_PACKAGE_EXPORTS(SUBPATH, PACKAGE_DIR)
1. Parse PACKAGE_DIR/package.json, and look for "exports" field.
2. If "exports" is null or undefined, return.
3. If `--no-require-module` is not enabled
  a. let CONDITIONS = ["node", "require", "module-sync"]
  b. Else, let CONDITIONS = ["node", "require"]
4. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(PACKAGE_DIR), "." + SUBPATH,
   `package.json` "exports", CONDITIONS) defined in the ESM resolver.
5. RESOLVE_ESM_MATCH(MATCH)

LOAD_PACKAGE_SELF(X, DIR)
1. Find the closest package scope SCOPE to DIR.
2. If no scope was found, return.
3. If the SCOPE/package.json "exports" is null or undefined, return.
4. If the SCOPE/package.json "name" is not the first segment of X, return.
5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE),
   "." + X.slice("name".length), `package.json` "exports", ["node", "require"])
   defined in the ESM resolver.
6. RESOLVE_ESM_MATCH(MATCH)

RESOLVE_ESM_MATCH(MATCH)
1. let RESOLVED_PATH = fileURLToPath(MATCH)
2. If the file at RESOLVED_PATH exists, load RESOLVED_PATH as its extension
   format. STOP
3. THROW "not found"
text

The "ESM resolver" is defined in the ESM documentation.

Caching#

Modules are cached after the first time they are loaded. This means (among other things) that every call to require('foo') will get exactly the same object returned, if it would resolve to the same file.

Provided require.cache is not modified, multiple calls to require('foo') will not cause the module code to be executed multiple times. This is an important feature. With it, "partially done" objects can be returned, thus allowing transitive dependencies to be loaded even when they would cause cycles.

To have a module execute code multiple times, export a function, and call that function.

Module caching caveats#

Modules are cached based on their resolved filename. Since modules may resolve to a different filename based on the location of the calling module (loading from node_modules folders), it is not a guarantee that require('foo') will always return the exact same object, if it would resolve to different files.

Additionally, on case-insensitive file systems or operating systems, different resolved filenames can point to the same file, but the cache will still treat them as different modules and will reload the file multiple times. For example, require('./foo') and require('./FOO') return two different objects, irrespective of whether or not ./foo and ./FOO are the same file.

Built-in modules#

Node.js has several modules compiled into the binary. These modules are described in greater detail elsewhere in this documentation.

The built-in modules are defined within the Node.js source and are located in the lib/ folder.

Built-in modules can be identified using the node: prefix, in which case it bypasses the require cache. For instance, require('node:http') will always return the built in HTTP module, even if there is require.cache entry by that name.

Some built-in modules are always preferentially loaded if their identifier is passed to require(). For instance, require('http') will always return the built-in HTTP module, even if there is a file by that name.

The list of all the built-in modules can be retrieved from module.builtinModules. The modules being all listed without the node: prefix, except those that mandate such prefix (as explained in the next section).

Built-in modules with mandatory node: prefix#

When being loaded by require(), some built-in modules must be requested with the node: prefix. This requirement exists to prevent newly introduced built-in modules from having a conflict with user land packages that already have taken the name. Currently the built-in modules that requires the node: prefix are:

The list of these modules is exposed in module.builtinModules, including the prefix.

Cycles#

When there are circular require() calls, a module might not have finished executing when it is returned.

Consider this situation:

a.js:

console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
js

b.js:

console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');
js

main.js:

console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done = %j, b.done = %j', a.done, b.done);
js

When main.js loads a.js, then a.js in turn loads b.js. At that point, b.js tries to load a.js. In order to prevent an infinite loop, an unfinished copy of the a.js exports object is returned to the b.js module. b.js then finishes loading, and its exports object is provided to the a.js module.

By the time main.js has loaded both modules, they're both finished. The output of this program would thus be:

$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done = true, b.done = true
console

Careful planning is required to allow cyclic module dependencies to work correctly within an application.

File modules#

If the exact filename is not found, then Node.js will attempt to load the required filename with the added extensions: .js, .json, and finally .node. When loading a file that has a different extension (e.g. .cjs), its full name must be passed to require(), including its file extension (e.g. require('./file.cjs')).

.json files are parsed as JSON text files, .node files are interpreted as compiled addon modules loaded with process.dlopen(). Files using any other extension (or no extension at all) are parsed as JavaScript text files. Refer to the Determining module system section to understand what parse goal will be used.

A required module prefixed with '/' is an absolute path to the file. For example, require('/home/marco/foo.js') will load the file at /home/marco/foo.js.

A required module prefixed with './' is relative to the file calling require(). That is, circle.js must be in the same directory as foo.js for require('./circle') to find it.

Without a leading '/', './', or '../' to indicate a file, the module must either be a core module or is loaded from a node_modules folder.

If the given path does not exist, require() will throw a MODULE_NOT_FOUND error.

Folders as modules#

Stability: 3 - Legacy: Use subpath exports or subpath imports instead.

There are three ways in which a folder may be passed to require() as an argument.

The first is to create a package.json file in the root of the folder, which specifies a main module. An example package.json file might look like this:

{ "name" : "some-library",
  "main" : "./lib/some-library.js" }
json

If this was in a folder at ./some-library, then require('./some-library') would attempt to load ./some-library/lib/some-library.js.

If there is no package.json file present in the directory, or if the "main" entry is missing or cannot be resolved, then Node.js will attempt to load an index.js or index.node file out of that directory. For example, if there was no package.json file in the previous example, then require('./some-library') would attempt to load:

  • ./some-library/index.js
  • ./some-library/index.node

If these attempts fail, then Node.js will report the entire module as missing with the default error:

Error: Cannot find module 'some-library'
console

In all three above cases, an import('./some-library') call would result in a ERR_UNSUPPORTED_DIR_IMPORT error. Using package subpath exports or subpath imports can provide the same containment organization benefits as folders as modules, and work for both require and import.

Loading from node_modules folders#

If the module identifier passed to require() is not a built-in module, and does not begin with '/', '../', or './', then Node.js starts at the directory of the current module, and adds /node_modules, and attempts to load the module from that location. Node.js will not append node_modules to a path already ending in node_modules.

If it is not found there, then it moves to the parent directory, and so on, until the root of the file system is reached.

For example, if the file at '/home/ry/projects/foo.js' called require('bar.js'), then Node.js would look in the following locations, in this order:

  • /home/ry/projects/node_modules/bar.js
  • /home/ry/node_modules/bar.js
  • /home/node_modules/bar.js
  • /node_modules/bar.js

This allows programs to localize their dependencies, so that they do not clash.

It is possible to require specific files or sub modules distributed with a module by including a path suffix after the module name. For instance require('example-module/path/to/file') would resolve path/to/file relative to where example-module is located. The suffixed path follows the same module resolution semantics.

Loading from the global folders#

If the NODE_PATH environment variable is set to a colon-delimited list of absolute paths, then Node.js will search those paths for modules if they are not found elsewhere.

On Windows, NODE_PATH is delimited by semicolons (;) instead of colons.

NODE_PATH was originally created to support loading modules from varying paths before the current module resolution algorithm was defined.

NODE_PATH is still supported, but is less necessary now that the Node.js ecosystem has settled on a convention for locating dependent modules. Sometimes deployments that rely on NODE_PATH show surprising behavior when people are unaware that NODE_PATH must be set. Sometimes a module's dependencies change, causing a different version (or even a different module) to be loaded as the NODE_PATH is searched.

Additionally, Node.js will search in the following list of GLOBAL_FOLDERS:

  • 1: $HOME/.node_modules
  • 2: $HOME/.node_libraries
  • 3: $PREFIX/lib/node

Where $HOME is the user's home directory, and $PREFIX is the Node.js configured node_prefix.

These are mostly for historic reasons.

It is strongly encouraged to place dependencies in the local node_modules folder. These will be loaded faster, and more reliably.

The module wrapper#

Before a module's code is executed, Node.js will wrap it with a function wrapper that looks like the following:

(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
js

By doing this, Node.js achieves a few things:

  • It keeps top-level variables (defined with var, const, or let) scoped to the module rather than the global object.
  • It helps to provide some global-looking variables that are actually specific to the module, such as:
    • The module and exports objects that the implementor can use to export values from the module.
    • The convenience variables __filename and __dirname, containing the module's absolute filename and directory path.

The module scope#

__dirname#

The directory name of the current module. This is the same as the path.dirname() of the __filename.

Example: running node example.js from /Users/mjr

console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr
js

__filename#

The file name of the current module. This is the current module file's absolute path with symlinks resolved.

For a main program this is not necessarily the same as the file name used in the command line.

See __dirname for the directory name of the current module.

Examples:

Running node example.js from /Users/mjr

console.log(__filename);
// Prints: /Users/mjr/example.js
console.log(__dirname);
// Prints: /Users/mjr
js

Given two modules: a and b, where b is a dependency of a and there is a directory structure of:

  • /Users/mjr/app/a.js
  • /Users/mjr/app/node_modules/b/b.js

References to __filename within b.js will return /Users/mjr/app/node_modules/b/b.js while references to __filename within a.js will return /Users/mjr/app/a.js.

exports#

A reference to the module.exports that is shorter to type. See the section about the exports shortcut for details on when to use exports and when to use module.exports.

module#

A reference to the current module, see the section about the module object. In particular, module.exports is used for defining what a module exports and makes available through require().

require(id)#

  • id <string> module name or path
  • Returns: <any> exported module content

Used to import modules, JSON, and local files. Modules can be imported from node_modules. Local modules and JSON files can be imported using a relative path (e.g. ./, ./foo, ./bar/baz, ../foo) that will be resolved against the directory named by __dirname (if defined) or the current working directory. The relative paths of POSIX style are resolved in an OS independent fashion, meaning that the examples above will work on Windows in the same way they would on Unix systems.

// Importing a local module with a path relative to the `__dirname` or current
// working directory. (On Windows, this would resolve to .\path\myLocalModule.)
const myLocalModule = require('./path/myLocalModule');

// Importing a JSON file:
const jsonData = require('./path/filename.json');

// Importing a module from node_modules or Node.js built-in module:
const crypto = require('node:crypto');
js
require.cache#

Modules are cached in this object when they are required. By deleting a key value from this object, the next require will reload the module. This does not apply to native addons, for which reloading will result in an error.

Adding or replacing entries is also possible. This cache is checked before built-in modules and if a name matching a built-in module is added to the cache, only node:-prefixed require calls are going to receive the built-in module. Use with care!

const assert = require('node:assert');
const realFs = require('node:fs');

const fakeFs = {};
require.cache.fs = { exports: fakeFs };

assert.strictEqual(require('fs'), fakeFs);
assert.strictEqual(require('node:fs'), realFs);
js
require.extensions#

Stability: 0 - Deprecated

Instruct require on how to handle certain file extensions.

Process files with the extension .sjs as .js:

require.extensions['.sjs'] = require.extensions['.js'];
js

Deprecated. In the past, this list has been used to load non-JavaScript modules into Node.js by compiling them on-demand. However, in practice, there are much better ways to do this, such as loading modules via some other Node.js program, or compiling them to JavaScript ahead of time.

Avoid using require.extensions. Use could cause subtle bugs and resolving the extensions gets slower with each registered extension.

require.main#

The Module object representing the entry script loaded when the Node.js process launched, or undefined if the entry point of the program is not a CommonJS module. See "Accessing the main module".

In entry.js script:

console.log(require.main);
js
node entry.js
bash
Module {
  id: '.',
  path: '/absolute/path/to',
  exports: {},
  filename: '/absolute/path/to/entry.js',
  loaded: false,
  children: [],
  paths:
   [ '/absolute/path/to/node_modules',
     '/absolute/path/node_modules',
     '/absolute/node_modules',
     '/node_modules' ] }
js
require.resolve(request[, options])#
  • request <string> The module path to resolve.
  • options <Object>
    • paths <string>[] Paths to resolve module location from. If present, these paths are used instead of the default resolution paths, with the exception of GLOBAL_FOLDERS like $HOME/.node_modules, which are always included. Each of these paths is used as a starting point for the module resolution algorithm, meaning that the node_modules hierarchy is checked from this location.
  • Returns: <string>

Use the internal require() machinery to look up the location of a module, but rather than loading the module, just return the resolved filename.

If the module can not be found, a MODULE_NOT_FOUND error is thrown.

require.resolve.paths(request)#

Returns an array containing the paths searched during resolution of request or null if the request string references a core module, for example http or fs.

The module object#

In each module, the module free variable is a reference to the object representing the current module. For convenience, module.exports is also accessible via the exports module-global. module is not actually a global but rather local to each module.

module.children#

The module objects required for the first time by this one.

module.exports#

The module.exports object is created by the Module system. Sometimes this is not acceptable; many want their module to be an instance of some class. To do this, assign the desired export object to module.exports. Assigning the desired object to exports will simply rebind the local exports variable, which is probably not what is desired.

For example, suppose we were making a module called a.js:

const EventEmitter = require('node:events');

module.exports = new EventEmitter();

// Do some work, and after some time emit
// the 'ready' event from the module itself.
setTimeout(() => {
  module.exports.emit('ready');
}, 1000);
js

Then in another file we could do:

const a = require('./a');
a.on('ready', () => {
  console.log('module "a" is ready');
});
js

Assignment to module.exports must be done immediately. It cannot be done in any callbacks. This does not work:

x.js:

setTimeout(() => {
  module.exports = { a: 'hello' };
}, 0);
js

y.js:

const x = require('./x');
console.log(x.a);
js
exports shortcut#

The exports variable is available within a module's file-level scope, and is assigned the value of module.exports before the module is evaluated.

It allows a shortcut, so that module.exports.f = ... can be written more succinctly as exports.f = .... However, be aware that like any variable, if a new value is assigned to exports, it is no longer bound to module.exports:

module.exports.hello = true; // Exported from require of module
exports = { hello: false };  // Not exported, only available in the module
js

When the module.exports property is being completely replaced by a new object, it is common to also reassign exports:

module.exports = exports = function Constructor() {
  // ... etc.
};
js

To illustrate the behavior, imagine this hypothetical implementation of require(), which is quite similar to what is actually done by require():

function require(/* ... */) {
  const module = { exports: {} };
  ((module, exports) => {
    // Module code here. In this example, define a function.
    function someFunc() {}
    exports = someFunc;
    // At this point, exports is no longer a shortcut to module.exports, and
    // this module will still export an empty default object.
    module.exports = someFunc;
    // At this point, the module will now export someFunc, instead of the
    // default object.
  })(module, module.exports);
  return module.exports;
}
js

module.filename#

The fully resolved filename of the module.

module.id#

The identifier for the module. Typically this is the fully resolved filename.

module.isPreloading#

  • Type: <boolean> true if the module is running during the Node.js preload phase.

module.loaded#

Whether or not the module is done loading, or is in the process of loading.

module.parent#

Stability: 0 - Deprecated: Please use require.main and module.children instead.

The module that first required this one, or null if the current module is the entry point of the current process, or undefined if the module was loaded by something that is not a CommonJS module (E.G.: REPL or import).

module.path#

The directory name of the module. This is usually the same as the path.dirname() of the module.id.

module.paths#

The search paths for the module.

module.require(id)#

The module.require() method provides a way to load a module as if require() was called from the original module.

In order to do this, it is necessary to get a reference to the module object. Since require() returns the module.exports, and the module is typically only available within a specific module's code, it must be explicitly exported in order to be used.

The Module object#

This section was moved to Modules: module core module.

Source map v3 support#

This section was moved to Modules: module core module.

Modules: ECMAScript modules#

Stability: 2 - Stable

Introduction#

ECMAScript modules are the official standard format to package JavaScript code for reuse. Modules are defined using a variety of import and export statements.

The following example of an ES module exports a function:

// addTwo.mjs
function addTwo(num) {
  return num + 2;
}

export { addTwo };
js

The following example of an ES module imports the function from addTwo.mjs:

// app.mjs
import { addTwo } from './addTwo.mjs';

// Prints: 6
console.log(addTwo(4));
js

Node.js fully supports ECMAScript modules as they are currently specified and provides interoperability between them and its original module format, CommonJS.

Enabling#

Node.js has two module systems: CommonJS modules and ECMAScript modules.

Authors can tell Node.js to interpret JavaScript as an ES module via the .mjs file extension, the package.json "type" field with a value "module", or the --input-type flag with a value of "module". These are explicit markers of code being intended to run as an ES module.

Inversely, authors can explicitly tell Node.js to interpret JavaScript as CommonJS via the .cjs file extension, the package.json "type" field with a value "commonjs", or the --input-type flag with a value of "commonjs".

When code lacks explicit markers for either module system, Node.js will inspect the source code of a module to look for ES module syntax. If such syntax is found, Node.js will run the code as an ES module; otherwise it will run the module as CommonJS. See Determining module system for more details.

Packages#

This section was moved to Modules: Packages.

import Specifiers#

Terminology#

The specifier of an import statement is the string after the from keyword, e.g. 'node:path' in import { sep } from 'node:path'. Specifiers are also used in export from statements, and as the argument to an import() expression.

There are three types of specifiers:

  • Relative specifiers like './startup.js' or '../config.mjs'. They refer to a path relative to the location of the importing file. The file extension is always necessary for these.

  • Bare specifiers like 'some-package' or 'some-package/shuffle'. They can refer to the main entry point of a package by the package name, or a specific feature module within a package prefixed by the package name as per the examples respectively. Including the file extension is only necessary for packages without an "exports" field.

  • Absolute specifiers like 'file:///opt/nodejs/config.js'. They refer directly and explicitly to a full path.

Bare specifier resolutions are handled by the Node.js module resolution and loading algorithm. All other specifier resolutions are always only resolved with the standard relative URL resolution semantics.

Like in CommonJS, module files within packages can be accessed by appending a path to the package name unless the package's package.json contains an "exports" field, in which case files within packages can only be accessed via the paths defined in "exports".

For details on these package resolution rules that apply to bare specifiers in the Node.js module resolution, see the packages documentation.

Mandatory file extensions#

A file extension must be provided when using the import keyword to resolve relative or absolute specifiers. Directory indexes (e.g. './startup/index.js') must also be fully specified.

This behavior matches how import behaves in browser environments, assuming a typically configured server.

URLs#

ES modules are resolved and cached as URLs. This means that special characters must be percent-encoded, such as # with %23 and ? with %3F.

file:, node:, and data: URL schemes are supported. A specifier like 'https://example.com/app.js' is not supported natively in Node.js unless using a custom HTTPS loader.

file: URLs#

Modules are loaded multiple times if the import specifier used to resolve them has a different query or fragment.

import './foo.mjs?query=1'; // loads ./foo.mjs with query of "?query=1"
import './foo.mjs?query=2'; // loads ./foo.mjs with query of "?query=2"
js

The volume root may be referenced via /, //, or file:///. Given the differences between URL and path resolution (such as percent encoding details), it is recommended to use url.pathToFileURL when importing a path.

data: imports#

data: URLs are supported for importing with the following MIME types:

  • text/javascript for ES modules
  • application/json for JSON
  • application/wasm for Wasm
import 'data:text/javascript,console.log("hello!");';
import _ from 'data:application/json,"world!"' with { type: 'json' };
js

data: URLs only resolve bare specifiers for builtin modules and absolute specifiers. Resolving relative specifiers does not work because data: is not a special scheme. For example, attempting to load ./foo from data:text/javascript,import "./foo"; fails to resolve because there is no concept of relative resolution for data: URLs.

node: imports#

node: URLs are supported as an alternative means to load Node.js builtin modules. This URL scheme allows for builtin modules to be referenced by valid absolute URL strings.

import fs from 'node:fs/promises';
js

Import attributes#

Import attributes are an inline syntax for module import statements to pass on more information alongside the module specifier.

import fooData from './foo.json' with { type: 'json' };

const { default: barData } =
  await import('./bar.json', { with: { type: 'json' } });
js

Node.js only supports the type attribute, for which it supports the following values:

Attribute type Needed for
'json' JSON modules
'text' Text modules

The type: 'json' attribute is mandatory when importing JSON modules. The type: 'text' attribute is mandatory when importing text modules.

Built-in modules#

Built-in modules provide named exports of their public API. A default export is also provided which is the value of the CommonJS exports. The default export can be used for, among other things, modifying the named exports. Named exports of built-in modules are updated only by calling module.syncBuiltinESMExports().

import EventEmitter from 'node:events';
const e = new EventEmitter();
js
import { readFile } from 'node:fs';
readFile('./foo.txt', (err, source) => {
  if (err) {
    console.error(err);
  } else {
    console.log(source);
  }
});
js
import fs, { readFileSync } from 'node:fs';
import { syncBuiltinESMExports } from 'node:module';
import { Buffer } from 'node:buffer';

fs.readFileSync = () => Buffer.from('Hello, ESM');
syncBuiltinESMExports();

fs.readFileSync === readFileSync;
js

When importing built-in modules, all the named exports (i.e. properties of the module exports object) are populated even if they are not individually accessed. This can make initial imports of built-in modules slightly slower compared to loading them with require() or process.getBuiltinModule(), where the module exports object is evaluated immediately, but some of its properties may only be initialized when first accessed individually.

import() expressions#

Dynamic import() provides an asynchronous way to import modules. It is supported in both CommonJS and ES modules, and can be used to load both CommonJS and ES modules.

import.meta#

The import.meta meta property is an Object that contains the following properties. It is only supported in ES modules.

import.meta.dirname#

  • Type: <string> The directory name of the current module.

This is the same as the path.dirname() of the import.meta.filename.

Caveat: only present on file: modules.

import.meta.filename#

  • Type: <string> The full absolute path and filename of the current module, with symlinks resolved.

This is the same as the url.fileURLToPath() of the import.meta.url.

Caveat only local modules support this property. Modules not using the file: protocol will not provide it.

import.meta.url#

  • Type: <string> The absolute file: URL of the module.

This is defined exactly the same as it is in browsers providing the URL of the current module file.

This enables useful patterns such as relative file loading:

import { readFileSync } from 'node:fs';
const buffer = readFileSync(new URL('./data.proto', import.meta.url));
js

import.meta.main#

Stability: 1.0 - Early development

  • Type: <boolean> true when the current module is the entry point of the current process; false otherwise.

Equivalent to require.main === module in CommonJS.

Analogous to Python's __name__ == "__main__".

export function foo() {
  return 'Hello, world';
}

function main() {
  const message = foo();
  console.log(message);
}

if (import.meta.main) main();
// `foo` can be imported from another module without possible side-effects from `main`
js

import.meta.resolve(specifier)#

Stability: 1.2 - Release candidate

  • specifier <string> The module specifier to resolve relative to the current module.
  • Returns: <string> The absolute URL string that the specifier would resolve to.

import.meta.resolve is a module-relative resolution function scoped to each module, returning the URL string.

const dependencyAsset = import.meta.resolve('component-lib/asset.css');
// file:///app/node_modules/component-lib/asset.css
import.meta.resolve('./dep.js');
// file:///app/dep.js
js

All features of the Node.js module resolution are supported. Dependency resolutions are subject to the permitted exports resolutions within the package.

Caveats:

  • This can result in synchronous file-system operations, which can impact performance similarly to require.resolve.
  • This feature is not available within custom loaders (it would create a deadlock).

Non-standard API:

When using the --experimental-import-meta-resolve flag, that function accepts a second argument:

  • parent <string> | <URL> An optional absolute parent module URL to resolve from. Default: import.meta.url

Interoperability with CommonJS#

import statements#

An import statement can reference an ES module or a CommonJS module. import statements are permitted only in ES modules, but dynamic import() expressions are supported in CommonJS for loading ES modules.

When importing CommonJS modules, the module.exports object is provided as the default export. Named exports may be available, provided by static analysis as a convenience for better ecosystem compatibility.

require#

The CommonJS module require currently only supports loading synchronous ES modules (that is, ES modules that do not use top-level await).

See Loading ECMAScript modules using require() for details.

CommonJS Namespaces#

CommonJS modules consist of a module.exports object which can be of any type.

To support this, when importing CommonJS from an ECMAScript module, a namespace wrapper for the CommonJS module is constructed, which always provides a default export key pointing to the CommonJS module.exports value.

In addition, a heuristic static analysis is performed against the source text of the CommonJS module to get a best-effort static list of exports to provide on the namespace from values on module.exports. This is necessary since these namespaces must be constructed prior to the evaluation of the CJS module.

These CommonJS namespace objects also provide the default export as a 'module.exports' named export, in order to unambiguously indicate that their representation in CommonJS uses this value, and not the namespace value. This mirrors the semantics of the handling of the 'module.exports' export name in require(esm) interop support.

When importing a CommonJS module, it can be reliably imported using the ES module default import or its corresponding sugar syntax:

import { default as cjs } from 'cjs';
// Identical to the above
import cjsSugar from 'cjs';

console.log(cjs);
console.log(cjs === cjsSugar);
// Prints:
//   <module.exports>
//   true
js

This Module Namespace Exotic Object can be directly observed either when using import * as m from 'cjs' or a dynamic import:

import * as m from 'cjs';
console.log(m);
console.log(m === await import('cjs'));
// Prints:
//   [Module] { default: <module.exports>, 'module.exports': <module.exports> }
//   true
js

For better compatibility with existing usage in the JS ecosystem, Node.js in addition attempts to determine the CommonJS named exports of every imported CommonJS module to provide them as separate ES module exports using a static analysis process.

For example, consider a CommonJS module written:

// cjs.cjs
exports.name = 'exported';
cjs

The preceding module supports named imports in ES modules:

import { name } from './cjs.cjs';
console.log(name);
// Prints: 'exported'

import cjs from './cjs.cjs';
console.log(cjs);
// Prints: { name: 'exported' }

import * as m from './cjs.cjs';
console.log(m);
// Prints:
//   [Module] {
//     default: { name: 'exported' },
//     'module.exports': { name: 'exported' },
//     name: 'exported'
//   }
js

As can be seen from the last example of the Module Namespace Exotic Object being logged, the name export is copied off of the module.exports object and set directly on the ES module namespace when the module is imported.

Live binding updates or new exports added to module.exports are not detected for these named exports.

The detection of named exports is based on common syntax patterns but does not always correctly detect named exports. In these cases, using the default import form described above can be a better option.

Named exports detection covers many common export patterns, reexport patterns and build tool and transpiler outputs. See merve for the exact semantics implemented.

Differences between ES modules and CommonJS#

No require, exports, or module.exports#

In most cases, the ES module import can be used to load CommonJS modules.

If needed, a require function can be constructed within an ES module using module.createRequire().

No __filename or __dirname#

These CommonJS variables are not available in ES modules.

__filename and __dirname use cases can be replicated via import.meta.filename and import.meta.dirname.

No Addon Loading#

Addons are not currently supported with ES module imports.

They can instead be loaded with module.createRequire() or process.dlopen.

No require.main#

To replace require.main === module, there is the import.meta.main API.

No require.resolve#

Relative resolution can be handled via new URL('./local', import.meta.url).

For a complete require.resolve replacement, there is the import.meta.resolve API.

Alternatively module.createRequire() can be used.

No NODE_PATH#

NODE_PATH is not part of resolving import specifiers. Please use symlinks if this behavior is desired.

No require.extensions#

require.extensions is not used by import. Module customization hooks can provide a replacement.

No require.cache#

require.cache is not used by import as the ES module loader has its own separate cache.

JSON modules#

JSON files can be referenced by import:

import packageConfig from './package.json' with { type: 'json' };
js

The with { type: 'json' } syntax is mandatory; see Import Attributes.

The imported JSON only exposes a default export. There is no support for named exports. A cache entry is created in the CommonJS cache to avoid duplication. The same object is returned in CommonJS if the JSON module has already been imported from the same path.

Text modules#

Stability: 1.0 - Early development

Text modules are available behind the --experimental-import-text flag.

Text files can be referenced by import:

import message from './message.txt' with { type: 'text' };
js

The with { type: 'text' } syntax is mandatory; see Import Attributes.

The imported text only exposes a default export whose value is the module source as a string.

Wasm modules#

Importing both WebAssembly module instances and WebAssembly source phase imports is supported.

Both of these integrations are in line with the ES Module Integration Proposal for WebAssembly.

Wasm Source Phase Imports#

Stability: 1.2 - Release candidate

The Source Phase Imports proposal allows the import source keyword combination to import a WebAssembly.Module object directly, instead of getting a module instance already instantiated with its dependencies.

This is useful when needing custom instantiations for Wasm, while still resolving and loading it through the ES module integration.

For example, to create multiple instances of a module, or to pass custom imports into a new instance of library.wasm:

import source libraryModule from './library.wasm';

const instance1 = await WebAssembly.instantiate(libraryModule, importObject1);

const instance2 = await WebAssembly.instantiate(libraryModule, importObject2);
js

In addition to the static source phase, there is also a dynamic variant of the source phase via the import.source dynamic phase import syntax:

const dynamicLibrary = await import.source('./library.wasm');

const instance = await WebAssembly.instantiate(dynamicLibrary, importObject);
js

JavaScript String Builtins#

Stability: 1.2 - Release candidate

When importing WebAssembly modules, the WebAssembly JS String Builtins Proposal is automatically enabled through the ESM Integration. This allows WebAssembly modules to directly use efficient compile-time string builtins from the wasm:js-string namespace.

For example, the following Wasm module exports a string getLength function using the wasm:js-string length builtin:

(module
  ;; Compile-time import of the string length builtin.
  (import "wasm:js-string" "length" (func $string_length (param externref) (result i32)))

  ;; Define getLength, taking a JS value parameter assumed to be a string,
  ;; calling string length on it and returning the result.
  (func $getLength (param $str externref) (result i32)
    local.get $str
    call $string_length
  )

  ;; Export the getLength function.
  (export "getLength" (func $get_length))
)
text
import { getLength } from './string-len.wasm';
getLength('foo'); // Returns 3.
js

Wasm builtins are compile-time imports that are linked during module compilation rather than during instantiation. They do not behave like normal module graph imports and they cannot be inspected via WebAssembly.Module.imports(mod) or virtualized unless recompiling the module using the direct WebAssembly.compile API with string builtins disabled.

String constants may also be imported from the wasm:js/string-constants builtin import URL, allowing static JS string globals to be defined:

(module
  (import "wasm:js/string-constants" "hello" (global $hello externref))
)
text

Importing a module in the source phase before it has been instantiated will also use the compile-time builtins automatically:

import source mod from './string-len.wasm';
const { exports: { getLength } } = await WebAssembly.instantiate(mod, {});
getLength('foo'); // Also returns 3.
js

Wasm Instance Phase Imports#

Stability: 1.1 - Active development

Instance imports allow any .wasm files to be imported as normal modules, supporting their module imports in turn.

For example, an index.js containing:

import * as M from './library.wasm';
console.log(M);
js

executed under:

node index.mjs
bash

would provide the exports interface for the instantiation of library.wasm.

Reserved Wasm Namespaces#

When importing WebAssembly module instances, they cannot use import module names or import/export names that start with reserved prefixes:

  • wasm-js: - reserved in all module import names, module names and export names.
  • wasm: - reserved in module import names and export names (imported module names are allowed in order to support future builtin polyfills).

Importing a module using the above reserved names will throw a WebAssembly.LinkError.

Top-level await#

The await keyword may be used in the top level body of an ECMAScript module.

Assuming an a.mjs with

export const five = await Promise.resolve(5);
js

And a b.mjs with

import { five } from './a.mjs';

console.log(five); // Logs `5`
js
node b.mjs # works
bash

If a top level await expression never resolves, the node process will exit with a 13 status code.

import { spawn } from 'node:child_process';
import { execPath } from 'node:process';

spawn(execPath, [
  '--input-type=module',
  '--eval',
  // Never-resolving Promise:
  'await new Promise(() => {})',
]).once('exit', (code) => {
  console.log(code); // Logs `13`
});
js

Loaders#

The former Loaders documentation is now at Modules: Customization hooks.

Resolution and loading algorithm#

Features#

The default resolver has the following properties:

  • FileURL-based resolution as is used by ES modules
  • Relative and absolute URL resolution
  • No default extensions
  • No folder mains
  • Bare specifier package resolution lookup through node_modules
  • Does not fail on unknown extensions or protocols
  • Can optionally provide a hint of the format to the loading phase

The default loader has the following properties

  • Support for builtin module loading via node: URLs
  • Support for "inline" module loading via data: URLs
  • Support for file: module loading
  • Fails on any other URL protocol
  • Fails on unknown extensions for file: loading (supports only .cjs, .js, and .mjs)

When the --experimental-package-map flag is enabled, bare specifier resolution first consults the package map configuration. If the importing module is within a mapped package and the specifier matches a declared dependency, the package map resolution takes precedence. See Package maps for details.

Resolution algorithm#

The algorithm to load an ES module specifier is given through the ESM_RESOLVE method below. It returns the resolved URL for a module specifier relative to a parentURL.

The resolution algorithm determines the full resolved URL for a module load, along with its suggested module format. The resolution algorithm does not determine whether the resolved URL protocol can be loaded, or whether the file extensions are permitted, instead these validations are applied by Node.js during the load phase (for example, if it was asked to load a URL that has a protocol that is not file:, data: or node:.

The algorithm also tries to determine the format of the file based on the extension (see ESM_FILE_FORMAT algorithm below). If it does not recognize the file extension (eg if it is not .mjs, .cjs, or .json), then a format of undefined is returned, which will throw during the load phase.

The algorithm to determine the module format of a resolved URL is provided by ESM_FILE_FORMAT, which returns the unique module format for any file. The "module" format is returned for an ECMAScript Module, while the "commonjs" format is used to indicate loading through the legacy CommonJS loader. Additional formats such as "addon" can be extended in future updates.

In the following algorithms, all subroutine errors are propagated as errors of these top-level routines unless stated otherwise.

defaultConditions is the conditional environment name array, ["node", "import"].

The resolver can throw the following errors:

  • Invalid Module Specifier: Module specifier is an invalid URL, package name or package subpath specifier.
  • Invalid Package Configuration: package.json configuration is invalid or contains an invalid configuration.
  • Invalid Package Target: Package exports or imports define a target module for the package that is an invalid type or string target.
  • Package Path Not Exported: Package exports do not define or permit a target subpath in the package for the given module.
  • Package Import Not Defined: Package imports do not define the specifier.
  • Module Not Found: The package or module requested does not exist.
  • Unsupported Directory Import: The resolved path corresponds to a directory, which is not a supported target for module imports.

Resolution Algorithm Specification#

ESM_RESOLVE(specifier, parentURL)

  1. Let resolved be undefined.
  2. If specifier is a valid URL, then
    1. Set resolved to the result of parsing and reserializing specifier as a URL.
  3. Otherwise, if specifier starts with "/", "./", or "../", then
    1. Set resolved to the URL resolution of specifier relative to parentURL.
  4. Otherwise, if specifier starts with "#", then
    1. Set resolved to the result of PACKAGE_IMPORTS_RESOLVE(specifier, parentURL, defaultConditions).
  5. Otherwise,
    1. Note: specifier is now a bare specifier.
    2. Set resolved the result of PACKAGE_RESOLVE(specifier, parentURL).
  6. Let format be undefined.
  7. If resolved is a "file:" URL, then
    1. If resolved contains any percent encodings of "/" or "\" ("%2F" and "%5C" respectively), then
      1. Throw an Invalid Module Specifier error.
    2. If the file at resolved is a directory, then
      1. Throw an Unsupported Directory Import error.
    3. If the file at resolved does not exist, then
      1. Throw a Module Not Found error.
    4. Set resolved to the real path of resolved, maintaining the same URL querystring and fragment components.
    5. Set format to the result of ESM_FILE_FORMAT(resolved).
  8. Otherwise,
    1. Set format the module format of the content type associated with the URL resolved.
  9. Return format and resolved to the loading phase

PACKAGE_RESOLVE(packageSpecifier, parentURL)

  1. Let packageName be undefined.
  2. If packageSpecifier is an empty string, then
    1. Throw an Invalid Module Specifier error.
  3. If packageSpecifier is a Node.js builtin module name, then
    1. Return the string "node:" concatenated with packageSpecifier.
  4. If packageSpecifier does not start with "@", then
    1. Set packageName to the substring of packageSpecifier until the first "/" separator or the end of the string.
  5. Otherwise,
    1. If packageSpecifier does not contain a "/" separator, then
      1. Throw an Invalid Module Specifier error.
    2. Set packageName to the substring of packageSpecifier until the second "/" separator or the end of the string.
  6. If packageName starts with "." or contains "\" or "%", then
    1. Throw an Invalid Module Specifier error.
  7. Let packageSubpath be "." concatenated with the substring of packageSpecifier from the position at the length of packageName.
  8. Let selfUrl be the result of PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL).
  9. If selfUrl is not undefined, return selfUrl.
  10. While parentURL is not the file system root,
    1. Let packageURL be the URL resolution of "node_modules/" concatenated with packageName, relative to parentURL.
    2. Set parentURL to the parent folder URL of parentURL.
    3. If the folder at packageURL does not exist, then
      1. Continue the next loop iteration.
    4. Let pjson be the result of READ_PACKAGE_JSON(packageURL).
    5. If pjson is not null and pjson.exports is not null or undefined, then
      1. Return the result of PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions).
    6. Otherwise, if packageSubpath is equal to ".", then
      1. If pjson.main is a string, then
        1. Return the URL resolution of main in packageURL.
    7. Otherwise,
      1. Return the URL resolution of packageSubpath in packageURL.
  11. Throw a Module Not Found error.

PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL)

  1. Let packageURL be the result of LOOKUP_PACKAGE_SCOPE(parentURL).
  2. If packageURL is null, then
    1. Return undefined.
  3. Let pjson be the result of READ_PACKAGE_JSON(packageURL).
  4. If pjson is null or if pjson.exports is null or undefined, then
    1. Return undefined.
  5. If pjson.name is equal to packageName, then
    1. Return the result of PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions).
  6. Otherwise, return undefined.

PACKAGE_EXPORTS_RESOLVE(packageURL, subpath, exports, conditions)

Note: This function is directly invoked by the CommonJS resolution algorithm.

  1. If exports is an Object with both a key starting with "." and a key not starting with ".", throw an Invalid Package Configuration error.
  2. If subpath is equal to ".", then
    1. Let mainExport be undefined.
    2. If exports is a String or Array, or an Object containing no keys starting with ".", then
      1. Set mainExport to exports.
    3. Otherwise if exports is an Object containing a "." property, then
      1. Set mainExport to exports["."].
    4. If mainExport is not undefined, then
      1. Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, mainExport, null, false, conditions).
      2. If resolved is not null or undefined, return resolved.
  3. Otherwise, if exports is an Object and all keys of exports start with ".", then
    1. Assert: subpath begins with "./".
    2. Let resolved be the result of PACKAGE_IMPORTS_EXPORTS_RESOLVE( subpath, exports, packageURL, false, conditions).
    3. If resolved is not null or undefined, return resolved.
  4. Throw a Package Path Not Exported error.

PACKAGE_IMPORTS_RESOLVE(specifier, parentURL, conditions)

Note: This function is directly invoked by the CommonJS resolution algorithm.

  1. Assert: specifier begins with "#".
  2. If specifier is exactly equal to "#", then
    1. Throw an Invalid Module Specifier error.
  3. Let packageURL be the result of LOOKUP_PACKAGE_SCOPE(parentURL).
  4. If packageURL is not null, then
    1. Let pjson be the result of READ_PACKAGE_JSON(packageURL).
    2. If pjson.imports is a non-null Object, then
      1. Let resolved be the result of PACKAGE_IMPORTS_EXPORTS_RESOLVE( specifier, pjson.imports, packageURL, true, conditions).
      2. If resolved is not null or undefined, return resolved.
  5. Throw a Package Import Not Defined error.

PACKAGE_IMPORTS_EXPORTS_RESOLVE(matchKey, matchObj, packageURL, isImports, conditions)

  1. If matchKey ends in "/", then
    1. Throw an Invalid Module Specifier error.
  2. If matchKey is a key of matchObj and does not contain "*", then
    1. Let target be the value of matchObj[matchKey].
    2. Return the result of PACKAGE_TARGET_RESOLVE(packageURL, target, null, isImports, conditions).
  3. Let expansionKeys be the list of keys of matchObj containing only a single "*", sorted by the sorting function PATTERN_KEY_COMPARE which orders in descending order of specificity.
  4. For each key expansionKey in expansionKeys, do
    1. Let patternBase be the substring of expansionKey up to but excluding the first "*" character.
    2. If matchKey starts with but is not equal to patternBase, then
      1. Let patternTrailer be the substring of expansionKey from the index after the first "*" character.
      2. If patternTrailer has zero length, or if matchKey ends with patternTrailer and the length of matchKey is greater than or equal to the length of expansionKey, then
        1. Let target be the value of matchObj[expansionKey].
        2. Let patternMatch be the substring of matchKey starting at the index of the length of patternBase up to the length of matchKey minus the length of patternTrailer.
        3. Return the result of PACKAGE_TARGET_RESOLVE(packageURL, target, patternMatch, isImports, conditions).
  5. Return null.

PATTERN_KEY_COMPARE(keyA, keyB)

  1. Assert: keyA contains only a single "*".
  2. Assert: keyB contains only a single "*".
  3. Let baseLengthA be the index of "*" in keyA.
  4. Let baseLengthB be the index of "*" in keyB.
  5. If baseLengthA is greater than baseLengthB, return -1.
  6. If baseLengthB is greater than baseLengthA, return 1.
  7. If the length of keyA is greater than the length of keyB, return -1.
  8. If the length of keyB is greater than the length of keyA, return 1.
  9. Return 0.

PACKAGE_TARGET_RESOLVE(packageURL, target, patternMatch, isImports, conditions)

  1. If target is a String, then
    1. If target does not start with "./", then
      1. If isImports is false, or if target starts with "../" or "/", or if target is a valid URL, then
        1. Throw an Invalid Package Target error.
      2. If patternMatch is a String, then
        1. Return PACKAGE_RESOLVE(target with every instance of "*" replaced by patternMatch, packageURL + "/").
      3. Return PACKAGE_RESOLVE(target, packageURL + "/").
    2. If target split on "/" or "\" contains any "", ".", "..", or "node_modules" segments after the first "." segment, case insensitive and including percent encoded variants, throw an Invalid Package Target error.
    3. Let resolvedTarget be the URL resolution of the concatenation of packageURL and target.
    4. Assert: packageURL is contained in resolvedTarget.
    5. If patternMatch is null, then
      1. Return resolvedTarget.
    6. If patternMatch split on "/" or "\" contains any "", ".", "..", or "node_modules" segments, case insensitive and including percent encoded variants, throw an Invalid Module Specifier error.
    7. Return the URL resolution of resolvedTarget with every instance of "*" replaced with patternMatch.
  2. Otherwise, if target is a non-null Object, then
    1. If target contains any index property keys, as defined in ECMA-262 6.1.7 Array Index, throw an Invalid Package Configuration error.
    2. For each property p of target, in object insertion order as,
      1. If p equals "default" or conditions contains an entry for p, then
        1. Let targetValue be the value of the p property in target.
        2. Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, targetValue, patternMatch, isImports, conditions).
        3. If resolved is equal to undefined, continue the loop.
        4. Return resolved.
    3. Return undefined.
  3. Otherwise, if target is an Array, then
    1. If _target.length is zero, return null.
    2. For each item targetValue in target, do
      1. Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, targetValue, patternMatch, isImports, conditions), continuing the loop on any Invalid Package Target error.
      2. If resolved is undefined, continue the loop.
      3. Return resolved.
    3. Return or throw the last fallback resolution null return or error.
  4. Otherwise, if target is null, return null.
  5. Otherwise throw an Invalid Package Target error.

ESM_FILE_FORMAT(url)

  1. Assert: url corresponds to an existing file.
  2. If url ends in ".mjs", then
    1. Return "module".
  3. If url ends in ".cjs", then
    1. Return "commonjs".
  4. If url ends in ".json", then
    1. Return "json".
  5. If url ends in ".wasm", then
    1. Return "wasm".
  6. If --experimental-addon-modules is enabled and url ends in ".node", then
    1. Return "addon".
  7. Let packageURL be the result of LOOKUP_PACKAGE_SCOPE(url).
  8. Let pjson be the result of READ_PACKAGE_JSON(packageURL).
  9. Let packageType be null.
  10. If pjson?.type is "module" or "commonjs", then
    1. Set packageType to pjson.type.
  11. If url ends in ".js", then
    1. If packageType is not null, then
      1. Return packageType.
    2. If the result of DETECT_MODULE_SYNTAX(source) is true, then
      1. Return "module".
    3. Return "commonjs".
  12. If url does not have any extension, then
    1. If packageType is "module" and the file at url contains the "application/wasm" content type header for a WebAssembly module, then
      1. Return "wasm".
    2. If packageType is not null, then
      1. Return packageType.
    3. If the result of DETECT_MODULE_SYNTAX(source) is true, then
      1. Return "module".
    4. Return "commonjs".
  13. Return undefined (will throw during load phase).

LOOKUP_PACKAGE_SCOPE(url)

  1. Let scopeURL be url.
  2. While scopeURL is not the file system root,
    1. Set scopeURL to the parent URL of scopeURL.
    2. If scopeURL ends in a "node_modules" path segment, return null.
    3. Let pjsonURL be the resolution of "package.json" within scopeURL.
    4. if the file at pjsonURL exists, then
      1. Return scopeURL.
  3. Return null.

READ_PACKAGE_JSON(packageURL)

  1. Let pjsonURL be the resolution of "package.json" within packageURL.
  2. If the file at pjsonURL does not exist, then
    1. Return null.
  3. If the file at packageURL does not parse as valid JSON, then
    1. Throw an Invalid Package Configuration error.
  4. Return the parsed JSON source of the file at pjsonURL.

DETECT_MODULE_SYNTAX(source)

  1. Parse source as an ECMAScript module.
  2. If the parse is successful, then
    1. If source contains top-level await, static import or export statements, or import.meta, return true.
    2. If source contains a top-level lexical declaration (const, let, or class) of any of the CommonJS wrapper variables (require, exports, module, __filename, or __dirname) then return true.
  3. Return false.

Customizing ESM specifier resolution algorithm#

Module customization hooks provide a mechanism for customizing the ESM specifier resolution algorithm. An example that provides CommonJS-style resolution for ESM specifiers is commonjs-extension-resolution-loader.

Modules: Packages#

Introduction#

A package is a folder tree described by a package.json file. The package consists of the folder containing the package.json file and all subfolders until the next folder containing another package.json file, or a folder named node_modules.

This page provides guidance for package authors writing package.json files along with a reference for the package.json fields defined by Node.js.

Determining module system#

Introduction#

Node.js will treat the following as ES modules when passed to node as the initial input, or when referenced by import statements or import() expressions:

  • Files with an .mjs extension.

  • Files with a .js extension when the nearest parent package.json file contains a top-level "type" field with a value of "module".

  • Strings passed in as an argument to --eval, or piped to node via STDIN, with the flag --input-type=module.

  • Code containing syntax only successfully parsed as ES modules, such as import or export statements or import.meta, with no explicit marker of how it should be interpreted. Explicit markers are .mjs or .cjs extensions, package.json "type" fields with either "module" or "commonjs" values, or the --input-type flag. Dynamic import() expressions are supported in either CommonJS or ES modules and would not force a file to be treated as an ES module. See Syntax detection.

Node.js will treat the following as CommonJS when passed to node as the initial input, or when referenced by import statements or import() expressions:

  • Files with a .cjs extension.

  • Files with a .js extension when the nearest parent package.json file contains a top-level field "type" with a value of "commonjs".

  • Strings passed in as an argument to --eval or --print, or piped to node via STDIN, with the flag --input-type=commonjs.

  • Files with a .js extension with no parent package.json file or where the nearest parent package.json file lacks a type field, and where the code can evaluate successfully as CommonJS. In other words, Node.js tries to run such "ambiguous" files as CommonJS first, and will retry evaluating them as ES modules if the evaluation as CommonJS fails because the parser found ES module syntax.

Writing ES module syntax in "ambiguous" files incurs a performance cost, and therefore it is encouraged that authors be explicit wherever possible. In particular, package authors should always include the "type" field in their package.json files, even in packages where all sources are CommonJS. Being explicit about the type of the package will future-proof the package in case the default type of Node.js ever changes, and it will also make things easier for build tools and loaders to determine how the files in the package should be interpreted.

Syntax detection#

Stability: 1.2 - Release candidate

Node.js will inspect the source code of ambiguous input to determine whether it contains ES module syntax; if such syntax is detected, the input will be treated as an ES module.

Ambiguous input is defined as:

  • Files with a .js extension or no extension; and either no controlling package.json file or one that lacks a type field.
  • String input (--eval or STDIN) when --input-typeis not specified.

ES module syntax is defined as syntax that would throw when evaluated as CommonJS. This includes the following:

  • import statements (but not import() expressions, which are valid in CommonJS).
  • export statements.
  • import.meta references.
  • await at the top level of a module.
  • Lexical redeclarations of the CommonJS wrapper variables (require, module, exports, __dirname, __filename).

Module resolution and loading#

Node.js has two types of module resolution and loading, chosen based on how the module is requested.

When a module is requested via require() (available by default in CommonJS modules, and can be dynamically generated using createRequire() in both CommonJS and ES Modules):

  • Resolution:
    • The resolution initiated by require() supports folders as modules.
    • When resolving a specifier, if no exact match is found, require() will try to add extensions (.js, .json, and finally .node) and then attempt to resolve folders as modules.
    • It does not support URLs as specifiers by default.
  • Loading:
    • .json files are treated as JSON text files.
    • .node files are interpreted as compiled addon modules loaded with process.dlopen().
    • .ts, .mts and .cts files are treated as TypeScript text files.
    • Files with any other extension, or without extensions, are treated as JavaScript text files.
    • require() can only be used to load ECMAScript modules from CommonJS modules if the ECMAScript module and its dependencies are synchronous (i.e. they do not contain top-level await).

When a module is requested via static import statements (only available in ES Modules) or import() expressions (available in both CommonJS and ES Modules):

  • Resolution:
    • The resolution of import/import() does not support folders as modules, directory indexes (e.g. './startup/index.js') must be fully specified.
    • It does not perform extension searching. A file extension must be provided when the specifier is a relative or absolute file URL.
    • It supports file:// and data: URLs as specifiers by default.
  • Loading:
    • .json files are treated as JSON text files. When importing JSON modules, an import type attribute is required (e.g. import json from './data.json' with { type: 'json' }).
    • .node files are interpreted as compiled addon modules loaded with process.dlopen(), if --experimental-addon-modules is enabled.
    • .ts, .mts and .cts files are treated as TypeScript text files.
    • It accepts only .js, .mjs, and .cjs extensions for JavaScript text files.
    • .wasm files are treated as WebAssembly modules.
    • Any other file extensions will result in a ERR_UNKNOWN_FILE_EXTENSION error. Additional file extensions can be facilitated via customization hooks.
    • import/import() can be used to load JavaScript CommonJS modules. Such modules are passed through merve to try to identify named exports, which are available if they can be determined through static analysis.

Regardless of how a module is requested, the resolution and loading process can be customized using customization hooks.

package.json and file extensions#

Within a package, the package.json "type" field defines how Node.js should interpret .js files. If a package.json file does not have a "type" field, .js files are treated as CommonJS.

A package.json "type" value of "module" tells Node.js to interpret .js files within that package as using ES module syntax.

The "type" field applies not only to initial entry points (node my-app.js) but also to files referenced by import statements and import() expressions.

// my-app.js, treated as an ES module because there is a package.json
// file in the same folder with "type": "module".

import './startup/init.js';
// Loaded as ES module since ./startup contains no package.json file,
// and therefore inherits the "type" value from one level up.

import 'commonjs-package';
// Loaded as CommonJS since ./node_modules/commonjs-package/package.json
// lacks a "type" field or contains "type": "commonjs".

import './node_modules/commonjs-package/index.js';
// Loaded as CommonJS since ./node_modules/commonjs-package/package.json
// lacks a "type" field or contains "type": "commonjs".
js

Files ending with .mjs are always loaded as ES modules regardless of the nearest parent package.json.

Files ending with .cjs are always loaded as CommonJS regardless of the nearest parent package.json.

import './legacy-file.cjs';
// Loaded as CommonJS since .cjs is always loaded as CommonJS.

import 'commonjs-package/src/index.mjs';
// Loaded as ES module since .mjs is always loaded as ES module.
js

The .mjs and .cjs extensions can be used to mix types within the same package:

  • Within a "type": "module" package, Node.js can be instructed to interpret a particular file as CommonJS by naming it with a .cjs extension (since both .js and .mjs files are treated as ES modules within a "module" package).

  • Within a "type": "commonjs" package, Node.js can be instructed to interpret a particular file as an ES module by naming it with an .mjs extension (since both .js and .cjs files are treated as CommonJS within a "commonjs" package).

--input-type flag#

Strings passed in as an argument to --eval (or -e), or piped to node via STDIN, are treated as ES modules when the --input-type=module flag is set.

node --input-type=module --eval "import { sep } from 'node:path'; console.log(sep);"

echo "import { sep } from 'node:path'; console.log(sep);" | node --input-type=module
bash

For completeness there is also --input-type=commonjs, for explicitly running string input as CommonJS. This is the default behavior if --input-type is unspecified.

Package entry points#

In a package's package.json file, two fields can define entry points for a package: "main" and "exports". Both fields apply to both ES module and CommonJS module entry points.

The "main" field is supported in all versions of Node.js, but its capabilities are limited: it only defines the main entry point of the package.

The "exports" provides a modern alternative to "main" allowing multiple entry points to be defined, conditional entry resolution support between environments, and preventing any other entry points besides those defined in "exports". This encapsulation allows module authors to clearly define the public interface for their package.

For new packages targeting the currently supported versions of Node.js, the "exports" field is recommended. For packages supporting Node.js 10 and below, the "main" field is required. If both "exports" and "main" are defined, the "exports" field takes precedence over "main" in supported versions of Node.js.

Conditional exports can be used within "exports" to define different package entry points per environment, including whether the package is referenced via require or via import. For more information about supporting both CommonJS and ES modules in a single package please consult the dual CommonJS/ES module packages section.

Existing packages introducing the "exports" field will prevent consumers of the package from using any entry points that are not defined, including the package.json (e.g. require('your-package/package.json')). This will likely be a breaking change.

To make the introduction of "exports" non-breaking, ensure that every previously supported entry point is exported. It is best to explicitly specify entry points so that the package's public API is well-defined. For example, a project that previously exported main, lib, feature, and the package.json could use the following package.exports:

{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/index": "./lib/index.js",
    "./lib/index.js": "./lib/index.js",
    "./feature": "./feature/index.js",
    "./feature/index": "./feature/index.js",
    "./feature/index.js": "./feature/index.js",
    "./package.json": "./package.json"
  }
}
json

Alternatively a project could choose to export entire folders both with and without extensioned subpaths using export patterns:

{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/*": "./lib/*.js",
    "./lib/*.js": "./lib/*.js",
    "./feature": "./feature/index.js",
    "./feature/*": "./feature/*.js",
    "./feature/*.js": "./feature/*.js",
    "./package.json": "./package.json"
  }
}
json

With the above providing backwards-compatibility for any minor package versions, a future major change for the package can then properly restrict the exports to only the specific feature exports exposed:

{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./feature/*.js": "./feature/*.js",
    "./feature/internal/*": null
  }
}
json

Main entry point export#

When writing a new package, it is recommended to use the "exports" field:

{
  "exports": "./index.js"
}
json

When the "exports" field is defined, all subpaths of the package are encapsulated and no longer available to importers. For example, require('pkg/subpath.js') throws an ERR_PACKAGE_PATH_NOT_EXPORTED error.

This encapsulation of exports provides more reliable guarantees about package interfaces for tools and when handling semver upgrades for a package. It is not a strong encapsulation since a direct require of any absolute subpath of the package such as require('/path/to/node_modules/pkg/subpath.js') will still load subpath.js.

All currently supported versions of Node.js and modern build tools support the "exports" field. For projects using an older version of Node.js or a related build tool, compatibility can be achieved by including the "main" field alongside "exports" pointing to the same module:

{
  "main": "./index.js",
  "exports": "./index.js"
}
json

Subpath exports#

When using the "exports" field, custom subpaths can be defined along with the main entry point by treating the main entry point as the "." subpath:

{
  "exports": {
    ".": "./index.js",
    "./submodule.js": "./src/submodule.js"
  }
}
json

Now only the defined subpath in "exports" can be imported by a consumer:

import submodule from 'es-module-package/submodule.js';
// Loads ./node_modules/es-module-package/src/submodule.js
js

While other subpaths will error:

import submodule from 'es-module-package/private-module.js';
// Throws ERR_PACKAGE_PATH_NOT_EXPORTED
js
Extensions in subpaths#

Package authors should provide either extensioned (import 'pkg/subpath.js') or extensionless (import 'pkg/subpath') subpaths in their exports. This ensures that there is only one subpath for each exported module so that all dependents import the same consistent specifier, keeping the package contract clear for consumers and simplifying package subpath completions.

Traditionally, packages tended to use the extensionless style, which has the benefits of readability and of masking the true path of the file within the package.

With import maps now providing a standard for package resolution in browsers and other JavaScript runtimes, using the extensionless style can result in bloated import map definitions. Explicit file extensions can avoid this issue by enabling the import map to utilize a packages folder mapping to map multiple subpaths where possible instead of a separate map entry per package subpath export. This also mirrors the requirement of using the full specifier path in relative and absolute import specifiers.

Path Rules and Validation for Export Targets#

When defining paths as targets in the "exports" field, Node.js enforces several rules to ensure security, predictability, and proper encapsulation. Understanding these rules is crucial for authors publishing packages.

Targets must be relative URLs#

All target paths in the "exports" map (the values associated with export keys) must be relative URL strings starting with ./.

// package.json
{
  "name": "my-package",
  "exports": {
    ".": "./dist/main.js",          // Correct
    "./feature": "./lib/feature.js", // Correct
    // "./origin-relative": "/dist/main.js", // Incorrect: Must start with ./
    // "./absolute": "file:///dev/null", // Incorrect: Must start with ./
    // "./outside": "../common/util.js" // Incorrect: Must start with ./
  }
}
json

Reasons for this behavior include:

  • Security: Prevents exporting arbitrary files from outside the package's own directory.
  • Encapsulation: Ensures all exported paths are resolved relative to the package root, making the package self-contained.
No path traversal or invalid segments#

Export targets must not resolve to a location outside the package's root directory. Additionally, path segments like . (single dot), .. (double dot), or node_modules (and their URL-encoded equivalents) are generally disallowed within the target string after the initial ./ and in any subpath part substituted into a target pattern.

// package.json
{
  "name": "my-package",
  "exports": {
    // ".": "./dist/../../elsewhere/file.js", // Invalid: path traversal
    // ".": "././dist/main.js",             // Invalid: contains "." segment
    // ".": "./dist/../dist/main.js",       // Invalid: contains ".." segment
    // "./utils/./helper.js": "./utils/helper.js" // Key has invalid segment
  }
}
json

Exports sugar#

If the "." export is the only export, the "exports" field provides sugar for this case being the direct "exports" field value.

{
  "exports": {
    ".": "./index.js"
  }
}
json

can be written:

{
  "exports": "./index.js"
}
json

Subpath imports#

In addition to the "exports" field, there is a package "imports" field to create private mappings that only apply to import specifiers from within the package itself.

Entries in the "imports" field must always start with # to ensure they are disambiguated from external package specifiers.

For example, the imports field can be used to gain the benefits of conditional exports for internal modules:

// package.json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
}
json

where import '#dep' does not get the resolution of the external package dep-node-native (including its exports in turn), and instead gets the local file ./dep-polyfill.js relative to the package in other environments.

Unlike the "exports" field, the "imports" field permits mapping to external packages.

The resolution rules for the imports field are otherwise analogous to the exports field.

Subpath patterns#

For packages with a small number of exports or imports, we recommend explicitly listing each exports subpath entry. But for packages that have large numbers of subpaths, this might cause package.json bloat and maintenance issues.

For these use cases, subpath export patterns can be used instead:

// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/*.js": "./src/features/*.js"
  },
  "imports": {
    "#internal/*.js": "./src/internal/*.js"
  }
}
json

* maps expose nested subpaths as it is a string replacement syntax only.

All instances of * on the right hand side will then be replaced with this value, including if it contains any / separators.

import featureX from 'es-module-package/features/x.js';
// Loads ./node_modules/es-module-package/src/features/x.js

import featureY from 'es-module-package/features/y/y.js';
// Loads ./node_modules/es-module-package/src/features/y/y.js

import internalZ from '#internal/z.js';
// Loads ./src/internal/z.js
js

This is a direct static matching and replacement without any special handling for file extensions. Including the "*.js" on both sides of the mapping restricts the exposed package exports to only JS files.

The property of exports being statically enumerable is maintained with exports patterns since the individual exports for a package can be determined by treating the right hand side target pattern as a ** glob against the list of files within the package. Because node_modules paths are forbidden in exports targets, this expansion is dependent on only the files of the package itself.

To exclude private subfolders from patterns, null targets can be used:

// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/*.js": "./src/features/*.js",
    "./features/private-internal/*": null
  }
}
json
import featureInternal from 'es-module-package/features/private-internal/m.js';
// Throws: ERR_PACKAGE_PATH_NOT_EXPORTED

import featureX from 'es-module-package/features/x.js';
// Loads ./node_modules/es-module-package/src/features/x.js
js

Conditional exports#

Conditional exports provide a way to map to different paths depending on certain conditions. They are supported for both CommonJS and ES module imports.

For example, a package that wants to provide different ES module exports for require() and import can be written:

// package.json
{
  "exports": {
    "import": "./index-module.js",
    "require": "./index-require.cjs"
  },
  "type": "module"
}
json

Node.js implements the following conditions, listed in order from most specific to least specific as conditions should be defined:

  • "node-addons" - similar to "node" and matches for any Node.js environment. This condition can be used to provide an entry point which uses native C++ addons as opposed to an entry point which is more universal and doesn't rely on native addons. This condition can be disabled via the --no-addons flag.
  • "node" - matches for any Node.js environment. Can be a CommonJS or ES module file. In most cases explicitly calling out the Node.js platform is not necessary.
  • "import" - matches when the package is loaded via import or import(), or via any top-level import or resolve operation by the ECMAScript module loader. Applies regardless of the module format of the target file. Always mutually exclusive with "require".
  • "require" - matches when the package is loaded via require(). The referenced file should be loadable with require() although the condition matches regardless of the module format of the target file. Expected formats include CommonJS, JSON, native addons, and ES modules. Always mutually exclusive with "import".
  • "module-sync" - matches no matter the package is loaded via import, import() or require(). The format is expected to be ES modules that does not contain top-level await in its module graph - if it does, ERR_REQUIRE_ASYNC_MODULE will be thrown when the module is require()-ed.
  • "default" - the generic fallback that always matches. Can be a CommonJS or ES module file. This condition should always come last.

Within the "exports" object, key order is significant. During condition matching, earlier entries have higher priority and take precedence over later entries. The general rule is that conditions should be from most specific to least specific in object order.

Using the "import" and "require" conditions can lead to some hazards, which are further explained in the dual CommonJS/ES module packages section.

The "node-addons" condition can be used to provide an entry point which uses native C++ addons. However, this condition can be disabled via the --no-addons flag. When using "node-addons", it's recommended to treat "default" as an enhancement that provides a more universal entry point, e.g. using WebAssembly instead of a native addon.

Conditional exports can also be extended to exports subpaths, for example:

{
  "exports": {
    ".": "./index.js",
    "./feature.js": {
      "node": "./feature-node.js",
      "default": "./feature.js"
    }
  }
}
json

Defines a package where require('pkg/feature.js') and import 'pkg/feature.js' could provide different implementations between Node.js and other JS environments.

When using environment branches, always include a "default" condition where possible. Providing a "default" condition ensures that any unknown JS environments are able to use this universal implementation, which helps avoid these JS environments from having to pretend to be existing environments in order to support packages with conditional exports. For this reason, using "node" and "default" condition branches is usually preferable to using "node" and "browser" condition branches.

Nested conditions#

In addition to direct mappings, Node.js also supports nested condition objects.

For example, to define a package that only has dual mode entry points for use in Node.js but not the browser:

{
  "exports": {
    "node": {
      "import": "./feature-node.mjs",
      "require": "./feature-node.cjs"
    },
    "default": "./feature.mjs"
  }
}
json

Conditions continue to be matched in order as with flat conditions. If a nested condition does not have any mapping it will continue checking the remaining conditions of the parent condition. In this way nested conditions behave analogously to nested JavaScript if statements.

Resolving user conditions#

When running Node.js, custom user conditions can be added with the --conditions flag:

node --conditions=development index.js
bash

which would then resolve the "development" condition in package imports and exports, while resolving the existing "node", "node-addons", "default", "import", and "require" conditions as appropriate.

Any number of custom conditions can be set with repeat flags.

Typical conditions should only contain alphanumerical characters, using ":", "-", or "=" as separators if necessary. Anything else may run into compatibility issues outside of node.

In node, conditions have very few restrictions, but specifically these include:

  1. They must contain at least one character.
  2. They cannot start with "." since they may appear in places that also allow relative paths.
  3. They cannot contain "," since they may be parsed as a comma-separated list by some CLI tools.
  4. They cannot be integer property keys like "10" since that can have unexpected effects on property key ordering for JS objects.

Community Conditions Definitions#

Condition strings other than the "import", "require", "node", "module-sync", "node-addons" and "default" conditions implemented in Node.js core are ignored by default.

Other platforms may implement other conditions and user conditions can be enabled in Node.js via the --conditions / -C flag.

Since custom package conditions require clear definitions to ensure correct usage, a list of common known package conditions and their strict definitions is provided below to assist with ecosystem coordination.

  • "types" - can be used by typing systems to resolve the typing file for the given export. This condition should always be included first.
  • "browser" - any web browser environment.
  • "development" - can be used to define a development-only environment entry point, for example to provide additional debugging context such as better error messages when running in a development mode. Must always be mutually exclusive with "production".
  • "production" - can be used to define a production environment entry point. Must always be mutually exclusive with "development".

For other runtimes, platform-specific condition key definitions are maintained by the WinterCG in the Runtime Keys proposal specification.

New conditions definitions may be added to this list by creating a pull request to the Node.js documentation for this section. The requirements for listing a new condition definition here are that:

  • The definition should be clear and unambiguous for all implementers.
  • The use case for why the condition is needed should be clearly justified.
  • There should exist sufficient existing implementation usage.
  • The condition name should not conflict with another condition definition or condition in wide usage.
  • The listing of the condition definition should provide a coordination benefit to the ecosystem that wouldn't otherwise be possible. For example, this would not necessarily be the case for company-specific or application-specific conditions.
  • The condition should be such that a Node.js user would expect it to be in Node.js core documentation. The "types" condition is a good example: It doesn't really belong in the Runtime Keys proposal but is a good fit here in the Node.js docs.

The above definitions may be moved to a dedicated conditions registry in due course.

Self-referencing a package using its name#

Within a package, the values defined in the package's package.json "exports" field can be referenced via the package's name. For example, assuming the package.json is:

// package.json
{
  "name": "a-package",
  "exports": {
    ".": "./index.mjs",
    "./foo.js": "./foo.js"
  }
}
json

Then any module in that package can reference an export in the package itself:

// ./a-module.mjs
import { something } from 'a-package'; // Imports "something" from ./index.mjs.
js

Self-referencing is available only if package.json has "exports", and will allow importing only what that "exports" (in the package.json) allows. So the code below, given the previous package, will generate a runtime error:

// ./another-module.mjs

// Imports "another" from ./m.mjs. Fails because
// the "package.json" "exports" field
// does not provide an export named "./m.mjs".
import { another } from 'a-package/m.mjs';
js

Self-referencing is also available when using require, both in an ES module, and in a CommonJS one. For example, this code will also work:

// ./a-module.js
const { something } = require('a-package/foo.js'); // Loads from ./foo.js.
cjs

Finally, self-referencing also works with scoped packages. For example, this code will also work:

// package.json
{
  "name": "@my/package",
  "exports": "./index.js"
}
json
// ./index.js
module.exports = 42;
cjs
// ./other.js
console.log(require('@my/package'));
cjs
$ node other.js
42
console

Dual CommonJS/ES module packages#

See the package examples repository for details.

Package maps#

Stability: 1 - Experimental. Enable this API with --experimental-package-map.

Package maps provide a mechanism to control package resolution without relying on the node_modules folder structure. When enabled via the --experimental-package-map flag, Node.js uses a JSON configuration file to determine how bare specifiers are resolved.

This feature is useful for:

  • Monorepos: Define explicit dependency relationships between workspace packages without symlinks or hoisting complexities.
  • Dependency isolation: Prevent packages from accessing undeclared dependencies (phantom dependencies).
  • Low file system coupling: The package resolution algorithm runs without inspecting the file system, relying instead on static data tables.

Configuration file format#

The package map configuration file is a JSON file with a packages object. Each key in packages is called a package ID and is a unique identifier for a package entry:

{
  "packages": {
    "app": {
      "url": "./packages/app",
      "dependencies": {
        "@myorg/utils": "utils",
        "@myorg/ui-lib": "ui-lib"
      }
    },
    "utils": {
      "url": "./packages/utils"
    },
    "ui-lib": {
      "url": "./packages/ui-lib",
      "dependencies": {
        "@myorg/utils": "utils"
      }
    }
  }
}
json

Each package entry has the following fields:

  • url <string> Required. An absolute or relative URL. This is parsed using the WHATWG URL API, using the configuration file URL as base. Only file: protocol is supported. Multiple packages are allowed to share the same URL; consumers must key module instances by both module url and package IDs to differentiate them.
  • dependencies <Object> An object mapping bare specifiers to package keys. Each key is the import name used in source code, and each value is the corresponding package key in the packages object. Defaults to an empty object.

Resolution algorithm#

When a bare specifier is encountered:

  1. Node.js determines which package performs the resolution request.
    • If possible the package ID for the importer file should be provided to the resolution algorithm.
    • Failing that, the resolution will check if the file path is within any package location decoded from its url.
  2. If no package ID is provided and the importing file is not within any mapped package, an ERR_PACKAGE_MAP_EXTERNAL_FILE error is thrown.
  3. Node.js looks up the specifier's package name in the importing package's dependencies object to find the corresponding package key.
  4. If found, the resolution algorithm locates the target package location from the package's url field in the package map.
  5. If the specifier is not in dependencies, a MODULE_NOT_FOUND error is thrown.
  6. The package location is forwarded to the regular Node.js resolution algorithm to finish the resolution (index.js, exports field, etc).

More details can be found in the resolution algorithm pseudo-code.

Multiple package versions#

Different packages can depend on different versions of the same package. Because dependencies maps bare specifiers to package keys, two packages can map the same specifier to different targets:

{
  "packages": {
    "app": {
      "url": "./app",
      "dependencies": {
        "component": "component-v2"
      }
    },
    "legacy": {
      "url": "./legacy",
      "dependencies": {
        "component": "component-v1"
      }
    },
    "component-v1": {
      "url": "./vendor/component-1.0.0"
    },
    "component-v2": {
      "url": "./vendor/component-2.0.0"
    }
  }
}
json

Both app and legacy can import 'component', but they resolve to different paths based on their declared dependencies.

Multiple packages for the same URL#

To address complex hoisting situations, multiple packages may share the same URL, which introduces ambiguity when determining which package an import originates from:

{
  "packages": {
    "app-old": {
      "url": "./app-old",
      "dependencies": {
        "lib": "lib-old"
      }
    },
    "app-new": {
      "url": "./app-new",
      "dependencies": {
        "lib": "lib-new"
      }
    },
    "lib-old": {
      "url": "./lib",
      "dependencies": {
        "react": "react-15"
      }
    },
    "lib-new": {
      "url": "./lib",
      "dependencies": {
        "react": "react-18"
      }
    }
  }
}
json

In the example above both lib-old and lib-new use the same ./lib folder to store their sources, the only difference being in which version of react they'll access when performing require calls or using import.

Because multiple package entries share the same URL, resolving a bare specifier from a file within that URL is ambiguous unless the originating package ID is known. If the package ID cannot be determined (for example, because the caller did not propagate it from a previous resolution), Node.js will throw an error rather than guess.

To support this pattern, implementers must key module instances by package ID and propagate it from each resolution result to subsequent resolution requests. This ensures that when lib requires react, the runtime knows whether the request comes from lib-old or lib-new and can select the correct dependency.

Interaction with other resolution#

Package maps only apply to bare specifiers that are not Node.js builtin modules. The following cases are not affected by package maps and continue to use standard resolution:

  • Relative paths or URLs (./ or ../).
  • Absolute paths or URLs.
  • Node.js builtin modules (node:fs, etc.).

Limitations#

  • Package maps must be a single static file; dynamic configuration is not supported.
  • Circular dependency detection is not performed by the package map resolver.
  • The package map file is loaded synchronously at startup.

Node.js package.json field definitions#

This section describes the fields used by the Node.js runtime. Other tools (such as npm) use additional fields which are ignored by Node.js and not documented here.

The following fields in package.json files are used in Node.js:

  • "name" - Relevant when using named imports within a package. Also used by package managers as the name of the package.
  • "main" - The default module when loading the package, if exports is not specified, and in versions of Node.js prior to the introduction of exports.
  • "type" - The package type determining whether to load .js files as CommonJS or ES modules.
  • "exports" - Package exports and conditional exports. When present, limits which submodules can be loaded from within the package.
  • "imports" - Package imports, for use by modules within the package itself.

"name"#

{
  "name": "package-name"
}
json

The "name" field defines your package's name. Publishing to the npm registry requires a name that satisfies certain requirements.

The "name" field can be used in addition to the "exports" field to self-reference a package using its name.

"main"#

{
  "main": "./index.js"
}
json

The "main" field defines the entry point of a package when imported by name via a node_modules lookup. Its value is a path.

The "exports" field, if it exists, takes precedence over the "main" field when importing the package by name.

It also defines the script that is used when the package directory is loaded via require().

// This resolves to ./path/to/directory/index.js.
require('./path/to/directory');
cjs

"type"#

The "type" field defines the module format that Node.js uses for all .js files that have that package.json file as their nearest parent.

Files ending with .js are loaded as ES modules when the nearest parent package.json file contains a top-level field "type" with a value of "module".

The nearest parent package.json is defined as the first package.json found when searching in the current folder, that folder's parent, and so on up until a node_modules folder or the volume root is reached.

// package.json
{
  "type": "module"
}
json
# In same folder as preceding package.json
node my-app.js # Runs as ES module
bash

If the nearest parent package.json lacks a "type" field, or contains "type": "commonjs", .js files are treated as CommonJS. If the volume root is reached and no package.json is found, .js files are treated as CommonJS.

import statements of .js files are treated as ES modules if the nearest parent package.json contains "type": "module".

// my-app.js, part of the same example as above
import './startup.js'; // Loaded as ES module because of package.json
js

Regardless of the value of the "type" field, .mjs files are always treated as ES modules and .cjs files are always treated as CommonJS.

"exports"#

{
  "exports": "./index.js"
}
json

The "exports" field allows defining the entry points of a package when imported by name loaded either via a node_modules lookup or a self-reference to its own name. It is supported in Node.js 12+ as an alternative to the "main" that can support defining subpath exports and conditional exports while encapsulating internal unexported modules.

Conditional Exports can also be used within "exports" to define different package entry points per environment, including whether the package is referenced via require or via import.

All paths defined in the "exports" must be relative file URLs starting with ./.

"imports"#

// package.json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
}
json

Entries in the imports field must be strings starting with #.

Package imports permit mapping to external packages.

This field defines subpath imports for the current package.

Modules: TypeScript#

Stability: 2 - Stable

Enabling#

There are two ways to enable runtime TypeScript support in Node.js:

  1. For full support of all of TypeScript's syntax and features, including using any version of TypeScript, use a third-party package.

  2. For lightweight support, you can use the built-in support for type stripping.

Full TypeScript support#

To use TypeScript with full support for all TypeScript features, including tsconfig.json, you can use a third-party package. These instructions use tsx as an example but there are many other similar libraries available.

  1. Install the package as a development dependency using whatever package manager you're using for your project. For example, with npm:

    npm install --save-dev tsx
    
    bash
  2. Then you can run your TypeScript code via:

    npx tsx your-file.ts
    
    bash

    Or alternatively, you can run with node via:

    node --import=tsx your-file.ts
    
    bash

Type stripping#

By default Node.js will execute TypeScript files that contains only erasable TypeScript syntax. Node.js will replace TypeScript syntax with whitespace, and no type checking is performed. To disable this feature, use the flag --no-strip-types.

Node.js ignores tsconfig.json files and therefore features that depend on settings within tsconfig.json, such as paths or converting newer JavaScript syntax to older standards, are intentionally unsupported. To get full TypeScript support, see Full TypeScript support.

The type stripping feature is designed to be lightweight. By intentionally not supporting syntaxes that require JavaScript code generation, and by replacing inline types with whitespace, Node.js can run TypeScript code without the need for source maps.

Type stripping is compatible with most versions of TypeScript but we recommend version 5.8 or newer with the following tsconfig.json settings:

{
  "compilerOptions": {
     "noEmit": true, // Optional - see note below
     "target": "esnext",
     "module": "nodenext",
     "rewriteRelativeImportExtensions": true,
     "erasableSyntaxOnly": true,
     "verbatimModuleSyntax": true
  }
}
json

Use the noEmit option if you intend to only execute *.ts files, for example a build script. You won't need this flag if you intend to distribute *.js files.

Determining module system#

Node.js supports both CommonJS and ES Modules syntax in TypeScript files. Node.js will not convert from one module system to another; if you want your code to run as an ES module, you must use import and export syntax, and if you want your code to run as CommonJS you must use require and module.exports.

  • .ts files will have their module system determined the same way as .js files. To use import and export syntax, add "type": "module" to the nearest parent package.json.
  • .mts files will always be run as ES modules, similar to .mjs files.
  • .cts files will always be run as CommonJS modules, similar to .cjs files.
  • .tsx files are unsupported.

As in JavaScript files, file extensions are mandatory in import statements and import() expressions: import './file.ts', not import './file'. Because of backward compatibility, file extensions are also mandatory in require() calls: require('./file.ts'), not require('./file'), similar to how the .cjs extension is mandatory in require calls in CommonJS files.

The tsconfig.json option allowImportingTsExtensions will allow the TypeScript compiler tsc to type-check files with import specifiers that include the .ts extension.

TypeScript features#

Since Node.js is only removing inline types, any TypeScript features that involve replacing TypeScript syntax with new JavaScript syntax will error.

The most prominent features that require transformation are:

  • Enum declarations
  • namespace with runtime code
  • parameter properties
  • import aliases

namespaces that do not contain runtime code are supported. This example will work correctly:

// This namespace is exporting a type
namespace TypeOnly {
   export type A = string;
}
ts

This will result in ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX error:

// This namespace is exporting a value
namespace A {
   export let x = 1
}
ts

Since Decorators are currently a TC39 Stage 3 proposal, they are not transformed and will result in a parser error. Node.js does not provide polyfills and thus will not support decorators until they are supported natively in JavaScript.

In addition, Node.js does not read tsconfig.json files and does not support features that depend on settings within tsconfig.json, such as paths or converting newer JavaScript syntax into older standards.

Importing types without type keyword#

Due to the nature of type stripping, the type keyword is necessary to correctly strip type imports. Without the type keyword, Node.js will treat the import as a value import, which will result in a runtime error. The tsconfig option verbatimModuleSyntax can be used to match this behavior.

This example will work correctly:

import type { Type1, Type2 } from './module.ts';
import { fn, type FnParams } from './fn.ts';
ts

This will result in a runtime error:

import { Type1, Type2 } from './module.ts';
import { fn, FnParams } from './fn.ts';
ts

Non-file forms of input#

Type stripping can be enabled for --eval and STDIN. The module system will be determined by --input-type, as it is for JavaScript.

TypeScript syntax is unsupported in the REPL, --check, and inspect.

Source maps#

Since inline types are replaced by whitespace, source maps are unnecessary for correct line numbers in stack traces; and Node.js does not generate them.

Type stripping in dependencies#

To discourage package authors from publishing packages written in TypeScript, Node.js refuses to handle TypeScript files inside folders under a node_modules path.

Paths aliases#

tsconfig "paths" won't be transformed and therefore produce an error. The closest feature available is subpath imports with the limitation that they need to start with #.

Net#

Stability: 2 - Stable

The node:net module provides an asynchronous network API for creating stream-based TCP or IPC servers (net.createServer()) and clients (net.createConnection()).

It can be accessed using:

import net from 'node:net';
const net = require('node:net');
javascript

IPC support#

The node:net module supports IPC with named pipes on Windows, and Unix domain sockets on other operating systems.

Identifying paths for IPC connections#

net.connect(), net.createConnection(), server.listen(), and socket.connect() take a path parameter to identify IPC endpoints.

On Unix, the local domain is also known as the Unix domain. The path is a file system pathname. It will throw an error when the length of pathname is greater than the length of sizeof(sockaddr_un.sun_path). Typical values are 107 bytes on Linux and 103 bytes on macOS. If a Node.js API abstraction creates the Unix domain socket, it will unlink the Unix domain socket as well. For example, net.createServer() may create a Unix domain socket and server.close() will unlink it. But if a user creates the Unix domain socket outside of these abstractions, the user will need to remove it. The same applies when a Node.js API creates a Unix domain socket but the program then crashes. In short, a Unix domain socket will be visible in the file system and will persist until unlinked. On Linux, You can use Unix abstract socket by adding \0 to the beginning of the path, such as \0abstract. The path to the Unix abstract socket is not visible in the file system and it will disappear automatically when all open references to the socket are closed.

On Windows, the local domain is implemented using a named pipe. The path must refer to an entry in \\?\pipe\ or \\.\pipe\. Any characters are permitted, but the latter may do some processing of pipe names, such as resolving .. sequences. Despite how it might look, the pipe namespace is flat. Pipes will not persist. They are removed when the last reference to them is closed. Unlike Unix domain sockets, Windows will close and remove the pipe when the owning process exits.

JavaScript string escaping requires paths to be specified with extra backslash escaping such as:

net.createServer().listen(
  path.join('\\\\?\\pipe', process.cwd(), 'myctl'));
js

Class: net.BlockList#

The BlockList object can be used with some network APIs to specify rules for disabling inbound or outbound access to specific IP addresses, IP ranges, or IP subnets.

blockList.addAddress(address[, type])#

Adds a rule to block the given IP address.

blockList.addAddresses(addresses[, type])#

Adds multiple address rules to the block list in a single operation. This is more efficient than calling blockList.addAddress() repeatedly when adding a large number of individual addresses, as the addresses are inserted under a single internal lock acquisition.

blockList.addCIDR(cidr)#

  • cidr <string> An IPv4 or IPv6 subnet in CIDR notation (e.g. '10.0.0.0/8' or '2001:db8::/32').

Adds a subnet rule using CIDR notation. The address family is automatically detected from the address (IPv6 if the address contains ':', IPv4 otherwise). This is equivalent to calling blockList.addSubnet() with the parsed network address, prefix length, and family.

blockList.addCIDRs(cidrs)#

  • cidrs <string>[] An array of IPv4 or IPv6 subnets in CIDR notation.

Adds multiple subnet rules using CIDR notation in a single call. The address family for each entry is automatically detected. This is equivalent to calling blockList.addCIDR() for each element of the array.

blockList.addRange(start, end[, type])#

Adds a rule to block a range of IP addresses from start (inclusive) to end (inclusive).

blockList.addSubnet(net, prefix[, type])#

  • net <string> | <net.SocketAddress> The network IPv4 or IPv6 address.
  • prefix <number> The number of CIDR prefix bits. For IPv4, this must be a value between 0 and 32. For IPv6, this must be between 0 and 128.
  • type <string> Either 'ipv4' or 'ipv6'. Default: 'ipv4'.

Adds a rule to block a range of IP addresses specified as a subnet mask.

blockList.check(address[, type])#

Returns true if the given IP address matches any of the rules added to the BlockList.

const blockList = new net.BlockList();
blockList.addAddress('123.123.123.123');
blockList.addRange('10.0.0.1', '10.0.0.10');
blockList.addSubnet('8592:757c:efae:4e45::', 64, 'ipv6');

console.log(blockList.check('123.123.123.123'));  // Prints: true
console.log(blockList.check('10.0.0.3'));  // Prints: true
console.log(blockList.check('222.111.111.222'));  // Prints: false

// IPv6 notation for IPv4 addresses works:
console.log(blockList.check('::ffff:7b7b:7b7b', 'ipv6')); // Prints: true
console.log(blockList.check('::ffff:123.123.123.123', 'ipv6')); // Prints: true
js

blockList.clear()#

Clears all rules from the BlockList.

blockList.fromJSON(value)#

Stability: 1.2 - Release candidate

const blockList = new net.BlockList();
const data = [
  'Subnet: IPv4 192.168.1.0/24',
  'Address: IPv4 10.0.0.5',
  'Range: IPv4 192.168.2.1-192.168.2.10',
  'Range: IPv4 10.0.0.1-10.0.0.10',
];
blockList.fromJSON(data);
blockList.fromJSON(JSON.stringify(data));
js
  • value Blocklist.rules

BlockList.isBlockList(value)#

  • value <any> Any JS value
  • Returns <boolean> true if the value is a net.BlockList.

BlockList.PRIVATE_RANGES#

A frozen array of CIDR strings representing private, loopback, and link-local IP address ranges. This can be passed to blockList.addCIDRs() to quickly populate a blocklist with all non-routable address ranges.

The included ranges are:

  • 10.0.0.0/8 — RFC 1918 private IPv4
  • 172.16.0.0/12 — RFC 1918 private IPv4
  • 192.168.0.0/16 — RFC 1918 private IPv4
  • 127.0.0.0/8 — IPv4 loopback
  • ::1/128 — IPv6 loopback
  • 169.254.0.0/16 — IPv4 link-local
  • fe80::/10 — IPv6 link-local
  • fc00::/7 — IPv6 unique local (ULA)
const blockList = new net.BlockList();
blockList.addCIDRs(net.BlockList.PRIVATE_RANGES);

console.log(blockList.check('10.0.0.1'));      // Prints: true
console.log(blockList.check('127.0.0.1'));     // Prints: true
console.log(blockList.check('8.8.8.8'));       // Prints: false
js

blockList.removeAddress(address[, type])#

Removes a rule that was previously added with blockList.addAddress(). The address must match exactly the value used when the rule was added. If the specified address does not exist, this is a no-op.

blockList.removeCIDR(cidr)#

  • cidr <string> An IPv4 or IPv6 subnet in CIDR notation (e.g. '10.0.0.0/8' or '2001:db8::/32').

Removes a subnet rule using CIDR notation. The address family is automatically detected from the address. This is equivalent to calling blockList.removeSubnet() with the parsed network address, prefix length, and family. If the specified subnet does not exist, this is a no-op.

blockList.removeRange(start, end[, type])#

Removes a rule that was previously added with blockList.addRange(). The start and end addresses must match exactly the values used when the rule was added. If the specified range does not exist, this is a no-op.

blockList.removeSubnet(net, prefix[, type])#

  • net <string> | <net.SocketAddress> The network IPv4 or IPv6 address.
  • prefix <number> The number of CIDR prefix bits. For IPv4, this must be a value between 0 and 32. For IPv6, this must be between 0 and 128.
  • type <string> Either 'ipv4' or 'ipv6'. Default: 'ipv4'.

Removes a rule that was previously added with blockList.addSubnet(). The network address and prefix must match exactly the values used when the rule was added. If the specified subnet does not exist, this is a no-op.

blockList.rules#

The list of rules added to the blocklist.

blockList.size#

The number of rules in the blocklist. This is equivalent to blockList.rules.length but does not allocate the rules array.

blockList.toJSON()#

Stability: 1.2 - Release candidate

  • Returns Blocklist.rules

Class: net.SocketAddress#

new net.SocketAddress([options])#

  • options <Object>
    • address <string> The network address as either an IPv4 or IPv6 string. Default: '127.0.0.1' if family is 'ipv4'; '::' if family is 'ipv6'.
    • family <string> One of either 'ipv4' or 'ipv6'. Default: 'ipv4'.
    • flowlabel <number> An IPv6 flow-label used only if family is 'ipv6'.
    • port <number> An IP port.

socketaddress.address#

socketaddress.family#

  • Type: <string> Either 'ipv4' or 'ipv6'.

socketaddress.flowlabel#

socketaddress.port#

SocketAddress.parse(input)#

  • input <string> An input string containing an IP address and optional port, e.g. 123.1.2.3:1234 or [1::1]:1234.
  • Returns: <net.SocketAddress> Returns a SocketAddress if parsing was successful. Otherwise returns undefined.

Class: net.Server#

This class is used to create a TCP or IPC server.

A listening TCP net.Server can be transferred to a worker thread by listing it in the transferList of a worker_threads postMessage() call. This moves the underlying listening socket to the receiving thread, where it resumes accepting connections. See Transferring TCP handles to other threads.

new net.Server([options][, connectionListener])#

net.Server is an EventEmitter with the following events:

Event: 'close'#

Emitted when the server closes. If connections exist, this event is not emitted until all connections are ended.

Event: 'connection'#

Emitted when a new connection is made. socket is an instance of net.Socket.

Event: 'error'#

Emitted when an error occurs. Unlike net.Socket, the 'close' event will not be emitted directly following this event unless server.close() is manually called. See the example in discussion of server.listen().

Event: 'listening'#

Emitted when the server has been bound after calling server.listen().

Event: 'drop'#

When the number of connections reaches the threshold of server.maxConnections, the server will drop new connections and emit 'drop' event instead. If it is a TCP server, the argument is as follows, otherwise the argument is undefined.

  • data <Object> The argument passed to event listener.
    • localAddress <string> Local address.
    • localPort <number> Local port.
    • localFamily <string> Local family.
    • remoteAddress <string> Remote address.
    • remotePort <number> Remote port.
    • remoteFamily <string> Remote IP family. 'IPv4' or 'IPv6'.

server.address()#

Returns the bound address, the address family name, and port of the server as reported by the operating system if listening on an IP socket (useful to find which port was assigned when getting an OS-assigned address): { port: 12346, family: 'IPv4', address: '127.0.0.1' }.

For a server listening on a pipe or Unix domain socket, the name is returned as a string.

const server = net.createServer((socket) => {
  socket.end('goodbye\n');
}).on('error', (err) => {
  // Handle errors here.
  throw err;
});

// Grab an arbitrary unused port.
server.listen(() => {
  console.log('opened server on', server.address());
});
js

server.address() returns null before the 'listening' event has been emitted or after calling server.close().

server.close([callback])#

Stops the server from accepting new connections and keeps existing connections. This function is asynchronous, the server is finally closed when all connections are ended and the server emits a 'close' event. The optional callback will be called once the 'close' event occurs. Unlike that event, it will be called with an Error as its only argument if the server was not open when it was closed.

server[Symbol.asyncDispose]()#

Calls server.close() and returns a promise that fulfills when the server has closed.

server.getConnections(callback)#

Asynchronously get the number of concurrent connections on the server. Works when sockets were sent to forks.

Callback should take two arguments err and count.

server.listen()#

Start a server listening for connections. A net.Server can be a TCP or an IPC server depending on what it listens to.

Possible signatures:

This function is asynchronous. When the server starts listening, the 'listening' event will be emitted. The last parameter callback will be added as a listener for the 'listening' event.

All listen() methods can take a backlog parameter to specify the maximum length of the queue of pending connections. The actual length will be determined by the OS through sysctl settings such as tcp_max_syn_backlog and somaxconn on Linux. The default value of this parameter is 511 (not 512).

All net.Socket are set to SO_REUSEADDR (see socket(7) for details).

The server.listen() method can be called again if and only if there was an error during the first server.listen() call or server.close() has been called. Otherwise, an ERR_SERVER_ALREADY_LISTEN error will be thrown.

One of the most common errors raised when listening is EADDRINUSE. This happens when another server is already listening on the requested port/path/handle. One way to handle this would be to retry after a certain amount of time:

server.on('error', (e) => {
  if (e.code === 'EADDRINUSE') {
    console.error('Address in use, retrying...');
    setTimeout(() => {
      server.close();
      server.listen(PORT, HOST);
    },