Upgrading & What’s Changed

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Node.js 26 (26.10.0, released 2026-09-21, the Current line that becomes LTS on 2026-10-28), with the differences of Node.js 24 "Krypton" (24.21.0, Active LTS until 2026-10-20, then Maintenance) and Node.js 22 "Jod" (22.23.3, Maintenance) called out; versions, dates and stability levels were checked against the official documentation on 2026-10-04, and every example below was run on Node.js 26, 24 and 22 (the outputs say which line printed what; the codemod commands were run once, on 26, because they only rewrite source files). It is the page to open when a project written for an older tutorial, or for the two books named in the bibliography, has to run on a current line: it explains how to plan an upgrade, how to make Node.js tell you what will break, which deprecations are documentation-only and which already print warnings or throw, what each major from 22 to 26 changed, and where the reference books (Samer Buna, Efficient Node.js, and Jonathan Wexler, Node.js Projects, both O’Reilly, 2025) no longer match the platform. Release dates and the support policy itself live in Release Lines & LTS; this page is about what changes in your code when you cross a line.

The version baseline

Table 1. Release lines relevant to an upgrade (dates from the Node.js release schedule, 2026-10-04)
Line Latest patch checked Bundled npm / V8 Phase on 2026-10-04 Key dates

Node.js 26

26.10.0 (2026-09-21)

npm 11.19.1 / V8 14.6

Current

Released 2026-05-05; LTS 2026-10-28; Maintenance 2027-10-20; end-of-life 2029-04-30. No codename assigned yet.

Node.js 25

25.9.0 (2026-03-31)

npm 11.12.1

End-of-life

Released 2025-10-15; end-of-life 2026-06-01.

Node.js 24 "Krypton"

24.21.0 (2026-09-07)

npm 11.19.0 / V8 13.6

Active LTS

Released 2025-05-06; LTS 2025-10-28; Maintenance 2026-10-20; end-of-life 2028-04-30.

Node.js 23

(no longer shipped)

-

End-of-life

Released 2024-10-16; end-of-life 2025-06-01.

Node.js 22 "Jod"

22.23.3 (2026-09-23)

npm 10.9.9 / V8 12.4

Maintenance

Released 2024-04-24; LTS 2024-10-29; Maintenance 2025-10-21; end-of-life 2027-04-30.

Node.js 20 "Iron"

20.20.2 (2026-03-24)

npm 10.8.2

End-of-life

End-of-life 2026-04-30.

Two facts shape every upgrade plan. First, from Node.js 27 the project moves to one major release per year, every line becomes LTS, and an alpha channel opens in October for early testing (the announcement is Evolving the Node.js Release Schedule); the "even numbers are production" rule ends with Node.js 26. Second, npm 12 exists (npm latest was 12.2.0 on 2026-10-03) but no Node.js line bundles it: Node.js 26.10.0 ships npm 11.19.1, so the npm version is an upgrade you control separately, covered in npm & Package Management.

Upgrading across majors

A Node.js major is the only release type that is allowed to break documented behaviour; minor and patch releases of a line keep the stability index promises (see the stability index in Globals & Web Platform APIs). Breaking changes are announced in three places, and the upgrade workflow below uses all of them.

Table 2. The three sources that announce breaking changes
Source What it tells you How to use it

Release blog post of the X.0.0 release

The notable changes, the deprecations and removals, and every commit labelled SEMVER-MAJOR.

Read it for each major you skip, in order: the posts for 22.0.0 through 26.0.0 are the "per-major highlights" below.

Migration guides (blog category "migrations")

A curated list of platform changes (supported platforms, OpenSSL, behaviour changes) plus matching codemods. Guides exist for v20 to v22 and v22 to v24; none existed yet for v24 to v26 on 2026-10-04.

Use them as the checklist, then fall back to the release posts for the newer lines.

The deprecations page of the API docs

Each DEP code with its type, the version where it changed type, and the replacement.

Search for the code printed in a warning; see the table in this page for the codes that matter most.

The workflow that keeps surprises small is the same for a service and for a library:

  1. Move one LTS to the next LTS at a time (22 to 24, then 24 to 26) while the service still has tests, and read the release post of every major in between, including the odd one that was skipped: Node.js 23 and 25 ended their lives as Current lines, but their SEMVER-MAJOR changes are part of the jump from 22 to 24 and from 24 to 26.

  2. Declare the supported range in package.json engines and run the test suite on every line you promise to support, locally with a version manager and in CI with a matrix; add the next Current line (or, from 27, the alpha channel) as an allowed-to-fail job so a breaking change shows up months before it reaches LTS.

  3. Make deprecations visible and fatal in the test run (the next section), upgrade dependencies that print them, and run codemods for the rest.

  4. Rebuild native add-ons: an add-on compiled against V8 or OpenSSL internals needs a rebuild for each major, while Node-API add-ons keep the same ABI across majors (see Native Add-ons, WebAssembly & FFI).

  5. Update the base image, the .nvmrc or .node-version file, and the engines field last, once the suite is green, and check the new line’s bundled npm (npm 10.9.9 on 22, 11.19.x on 24 and 26) against the lockfile version your team expects.

The matrix part of that workflow is a plain loop when a version manager is installed. This script runs a project’s strict test script on each line and reports the exit code; the project is a tiny Bookshelf check with one test that calls the legacy url.parse().

A local line-by-line test matrix with nvm
#!/usr/bin/env bash
source ~/.nvm/nvm.sh
for v in 22 24 26; do
  nvm use "$v" >/dev/null
  npm run --silent test:strict >/dev/null 2>&1
  status=$?
  echo "$(node -v): npm run test:strict exits with $status"
done
package.json of the checked project
{
  "name": "upgrade-check",
  "type": "module",
  "engines": { "node": ">=22.12" },
  "scripts": {
    "test": "node --test",
    "test:strict": "node --throw-deprecation --test"
  }
}
Output of the matrix script
v22.23.3: npm run test:strict exits with 0
v24.21.0: npm run test:strict exits with 1
v26.10.0: npm run test:strict exits with 1

The same test passes with plain npm test on all three lines (observed), and test:strict is the only script that fails on 24 and 26. The reason is in the next section: url.parse() is a documentation-only deprecation on 22 and a warning-printing one on 24 and 26.

Making Node.js show what will break

Node.js has four kinds of deprecation, and they differ in what you see:

Table 3. The four deprecation types
Type Behaviour How to surface it

Documentation-only

Appears only in the API docs and generates nothing at run time.

Some are marked as supporting --pending-deprecation; with that flag (or NODE_PENDING_DEPRECATION=1) they emit a warning like a runtime deprecation.

Application (non-node_modules code only)

A process warning printed once, the first time the API is used from code that is not under node_modules.

With --pending-deprecation it also warns for code loaded from node_modules.

Runtime (all code)

A process warning printed once for any caller, including dependencies.

--throw-deprecation turns it into a thrown error; --trace-deprecation adds a stack trace.

End-of-Life

The API was removed or now throws; the code needs a replacement before the upgrade.

Look for ERR_ codes, undefined functions and ERR_UNKNOWN_BUILTIN_MODULE.

The flags that control all of this are Stable command-line options: --trace-deprecation, --throw-deprecation, --pending-deprecation and --no-deprecation (and their NODE_OPTIONS forms), and --disable-warning=code-or-type, which became Stable in v26.7.0 and v24.20.0 (it was added in v21.3.0). The script below calls two APIs that are deprecated differently, and prints the stack of each warning with --trace-deprecation:

A script that triggers a runtime and an application deprecation
import { isArray } from 'node:util';   // DEP0044, runtime since v22.0.0
import { parse } from 'node:url';      // DEP0169, application since v24.0.0

console.log('isArray:', isArray([]));
console.log('host:', parse('https://example.com:8080/a?b=1').host);
Default run on Node.js 22.23.3: only the runtime deprecation warns
isArray: true
host: example.com:8080
(node:88840) [DEP0044] DeprecationWarning: The `util.isArray` API is deprecated. Please use `Array.isArray()` instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
Default run on Node.js 24.21.0 and 26.10.0: both warn
isArray: true
host: example.com:8080
(node:89139) [DEP0044] DeprecationWarning: The `util.isArray` API is deprecated. Please use `Array.isArray()` instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
(node:89139) [DEP0169] DeprecationWarning: `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.
With node --trace-deprecation on Node.js 26.10.0 (paths shortened)
(node:90213) [DEP0044] DeprecationWarning: The `util.isArray` API is deprecated. Please use `Array.isArray()` instead.
    at file:///…/dep-demo.mjs:4:25
    at ModuleJob.run (node:internal/modules/esm/module_job:569:25)
    …
(node:90213) [DEP0169] DeprecationWarning: `url.parse()` behavior is not standardized and prone to errors that have security implications. …
    at urlParse (node:url:137:13)
    at file:///…/dep-demo.mjs:5:22
    …

With --throw-deprecation the first warning is thrown as an exception whose code is DEP0044, so a CI job fails at the offending line; with --disable-warning=DEP0044 --disable-warning=DEP0169 or --no-deprecation the run is silent (all four variants were run on 26.10.0). Silencing is a tool for a dependency you cannot change yet, not a way to finish an upgrade.

Process warnings are also delivered to the 'warning' event on process, which makes a deprecation audit a small script that works even when the output is silenced (the --no-warnings flag below only hides the printing, not the event):

Collecting deprecation codes with the process warning event
import { createHash } from 'node:crypto';
import { parse } from 'node:url';

const codes = new Set();
process.on('warning', (warning) => {
  if (warning.name === 'DeprecationWarning') codes.add(warning.code);
});

createHash('shake128').update('bookshelf').digest('hex'); // DEP0198
parse('https://example.com/books?id=1');                  // DEP0169
new Buffer(4);                                            // DEP0005

process.on('exit', () => {
  console.log([...codes].sort().join(' ') || 'no deprecations');
  process.exitCode = codes.size > 0 ? 1 : 0;
});
Table 4. Codes collected by node --no-warnings audit.mjs, with and without --pending-deprecation
Node.js Default run With --pending-deprecation

22.23.3

DEP0005

DEP0005 DEP0169 DEP0198

24.21.0

DEP0005 DEP0169

DEP0005 DEP0169 DEP0198

26.10.0

DEP0005 DEP0169 DEP0198

DEP0005 DEP0169 DEP0198

Read that table as a forecast: the code that is silent on the line you run today is the code that warns on the next one, so running the suite with --pending-deprecation on Node.js 22 already shows the deprecations that 24 and 26 will print. (new Buffer() is an application deprecation that has been printing for years; the SHAKE example became a runtime deprecation in v25.0.0.)

Userland Migrations and codemods

Node.js maintains official codemods for "userland" code, the code outside the node executable. They are published under the @nodejs scope of the Codemod registry, are reviewed or written by Node.js members, and cover the end-of-life deprecations and breaking changes that have a mechanical fix. The Learn page recommends running them on a separate branch, reviewing the diff, running the tests, and formatting or linting afterwards. Migration guides list per-major codemods; for example, the guides name @nodejs/import-assertions-to-attributes for v20 to v22, and @nodejs/crypto-rsa-pss-update, @nodejs/dirent-path-to-parent-path, @nodejs/fs-access-mode-constants, @nodejs/fs-truncate-fd-deprecation, @nodejs/http2-priority-signaling, @nodejs/process-assert-to-node-assert and @nodejs/tls-create-secure-pair-to-tls-socket for v22 to v24. The deprecations page also prints the command next to the DEP codes that have one (for example DEP0044 and DEP0030). The Learn section additionally ships guides that replace third-party packages with built-ins: chalk, kleur and ansi-colors to util.styleText(), axios to the WHATWG fetch, and mocha to the Node.js test runner, plus one that corrects TypeScript import specifiers.

The Bookshelf scratch project had a file that still used the removed import-assertion syntax and one that used util.isArray():

Before running the codemods
// old.mjs
import data from './data.json' assert { type: 'json' };
console.log(data.name);

// legacy2.mjs
import { isArray } from 'node:util';

console.log(isArray([1, 2]));
Running two official codemods on a clean git tree (codemod CLI 1.18.3)
npx codemod@latest run @nodejs/import-assertions-to-attributes --no-interactive --disable-analytics
npx codemod@latest run @nodejs/util-is --no-interactive --disable-analytics
After the codemods
// old.mjs
import data from './data.json' with { type: 'json' };
console.log(data.name);

// legacy2.mjs
console.log(Array.isArray([1, 2]));

Three observations from running them, labelled as observed behaviour of that day’s CLI and registry packages and not as a promise. The Learn page shows npx codemod <codemod-name>; the CLI version used here needed the run subcommand, and without --no-interactive it stopped with "The input device is not a TTY". It refuses to run on a dirty git tree unless you add --allow-dirty, and --dry-run reports "Would modify 1 file" without writing. Finally, when a file had two separate node:util import lines (one for isArray, one for _extend), both the util-is and the util-extend-to-object-assign codemods removed both imports and left the other identifier unresolved. That is why the Learn page insists on reviewing every diff and running the tests after a codemod.

The deprecations that matter

The full list has more than two hundred entries, most of them old end-of-life ones. The table below keeps the codes that an application written for Node.js 18 to 22 is most likely to meet when moving to 24 and 26, in the state recorded by the 26.x API documentation. "Since" gives the version where the entry reached its current type; the history column shows the earlier steps.

Table 5. Deprecations that changed type between Node.js 22 and 26
Code API Current type and since Earlier steps and replacement

DEP0005

Buffer() / new Buffer()

Application, since v10.0.0

Use Buffer.alloc(), Buffer.allocUnsafe() or Buffer.from(); still prints on every line.

DEP0030

SlowBuffer

End-of-Life, v25.0.0

Runtime in v24.0.0 (prints on 24). Use Buffer.allocUnsafeSlow().

DEP0040

node:punycode

Application, v23.7.0 / v22.14.0

Runtime in v21.0.0, then relaxed to application scope. Use a userland alternative.

DEP0044

util.isArray()

Runtime, v22.0.0

Use Array.isArray(). Codemod: @nodejs/util-is.

DEP0045 to DEP0059

util.isBoolean(), isBuffer(), isDate(), isError(), isFunction(), isNull(), isNullOrUndefined(), isNumber(), isObject(), isPrimitive(), isRegExp(), isString(), isSymbol(), isUndefined(), util.log()

End-of-Life, v23.0.0

Runtime on 22.0.0. Replace with typeof, Array.isArray(), instanceof or a logger; util.isArray() is the only one that was not removed.

DEP0060

util._extend()

Runtime, v22.0.0

Use Object.assign(). Codemod: @nodejs/util-extend-to-object-assign.

DEP0063

ServerResponse.prototype.writeHeader()

End-of-Life, v26.0.0

Runtime in v25.0.0. Use writeHead().

DEP0147

fs.rmdir(path, { recursive: true })

End-of-Life, v25.0.0

Use fs.rm(path, { recursive: true, force: true }). Codemod: @nodejs/rmdir.

DEP0169

url.parse() (and url.format(string), url.resolve())

Application, v24.0.0

Documentation-only since v19.9.0 / v18.17.0, with --pending-deprecation support. Use the WHATWG URL. Application scope means it warns only in code outside node_modules.

DEP0176

fs.F_OK, fs.R_OK, fs.W_OK, fs.X_OK

End-of-Life, v25.0.0

Runtime in v24.0.0. Use fs.constants.F_OK and the others. Codemod: @nodejs/fs-access-mode-constants.

DEP0182

Short GCM authentication tags without an explicit authTagLength

End-of-Life, v26.0.0

Runtime in v23.0.0. Pass authTagLength, and keep the default 16-byte tag.

DEP0184

node:zlib classes called without new

Runtime, v24.0.0

Documentation-only in v22.9.0. Use new.

DEP0185

node:repl classes called without new

End-of-Life, v25.0.0

Runtime in v24.0.0.

DEP0187

Invalid argument types to fs.existsSync()

Runtime, v24.0.0

Documentation-only in v23.4.0 / v22.13.0; it will throw in a future version.

DEP0190

An args array together with shell: true in execFile() / spawn()

Runtime, v24.0.0

Documentation-only in v23.11.0 / v22.15.0. Arguments are only space-joined, so they are not escaped; drop the shell and keep the array.

DEP0193

require('node:_stream_*')

End-of-Life, v26.0.0

Runtime in v24.2.0. Use node:stream. (DEP0125 for _stream_wrap ended in v26.0.0 too.)

DEP0195

node:http classes called without new

Documentation-only, v24.2.0 / v22.17.0

Codemod: @nodejs/http-classes-with-new.

DEP0197

util.types.isNativeError()

Documentation-only, v24.2.0

Use Error.isError().

DEP0198

SHAKE-128 and SHAKE-256 without outputLength

Runtime, v25.0.0

Documentation-only with --pending-deprecation in v24.4.0 / v22.18.0. Pass outputLength.

DEP0201

options.type in Duplex.toWeb()

Runtime, v26.0.0

Documentation-only in v25.7.0. Use readableType.

DEP0203 and DEP0204

Passing a CryptoKey to node:crypto APIs; KeyObject.from() with a non-extractable CryptoKey

Runtime, v26.0.0

Documentation-only in v25.9.0; both will throw in a future version.

DEP0205

module.register()

Runtime, v26.0.0

Documentation-only in v25.9.0. Use module.registerHooks() (Stability 1.2, Release candidate).

DEP0206

hmac.digest() on an already-finalized Hmac

Documentation-only, v26.2.0

Today it returns an empty buffer; it will throw in a future version.

DEP0209

Using AbortSignal to dispose of long-lived resources

Documentation-only, v26.8.2

Prefer using / await using; AbortSignal remains the right tool for cancellation and timeouts.

Two of those entries have a nuance that older summaries get wrong. DEP0169 is an application deprecation, not a runtime one (a blog post for 24.0.0 calls it a runtime deprecation, but the API documentation, which is authoritative, lists the type as application); that is why a dependency in node_modules can call url.parse() silently unless --pending-deprecation is set. And DEP0190 has been a runtime deprecation since v24.0.0, which is the first time the warning is visible without a flag. Two entries were revoked and are safe to keep using: DEP0033, EventEmitter.listenerCount() (revoked in v25.4.0), and DEP0163, channel.subscribe(onMessage) (revoked in v24.8.0); DEP0116, the legacy URL API as a whole, was revoked earlier and its remaining parts are covered by DEP0169.

The script that follows checks, on each line, which of the removed members are still there. It was run with --no-warnings so that only the table is printed, which is why Node.js 22 and 24 show them as present although most of them print a warning there.

Probing removed APIs
import buffer from 'node:buffer';
import fs from 'node:fs';
import http from 'node:http';
import { mkdtempSync, mkdirSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

const results = {};
results['buffer.SlowBuffer'] = typeof buffer.SlowBuffer;
results['fs.F_OK'] = typeof fs.F_OK;
results['ServerResponse#writeHeader'] = typeof http.ServerResponse.prototype.writeHeader;
results['fs.constants.F_OK'] = typeof fs.constants.F_OK;

const dir = mkdtempSync(join(tmpdir(), 'rm-'));
mkdirSync(join(dir, 'a', 'b'), { recursive: true });
try {
  fs.rmdirSync(join(dir, 'a'), { recursive: true });
  results['rmdirSync recursive'] = 'removed the tree';
} catch (err) {
  results['rmdirSync recursive'] = err.code;
}
fs.rmSync(dir, { recursive: true, force: true });

for (const name of ['node:_stream_readable', 'node:_stream_wrap']) {
  try { await import(name); results[name] = 'loads'; }
  catch (err) { results[name] = err.code; }
}
console.table(results);
Table 6. Results of the removed-API probe (node --no-warnings)
Probe Node.js 22.23.3 Node.js 24.21.0 Node.js 26.10.0

buffer.SlowBuffer

function

function

undefined

fs.F_OK

number

number

undefined (fs.constants.F_OK is still a number)

ServerResponse.prototype.writeHeader

function

function

undefined

fs.rmdirSync(dir, { recursive: true })

removed the tree

removed the tree

throws ERR_INVALID_ARG_VALUE

node:_stream_readable, node:_stream_wrap

load

load

ERR_UNKNOWN_BUILTIN_MODULE

A second probe confirms the Node.js 23 removals: on 22.23.3 util.isBoolean, util.log and process.assert are still functions, and on 24.21.0 and 26.10.0 they are undefined, while util.isArray is still a function on all three; crypto.createCipher (removed in 22.0.0, DEP0106) is undefined on all three. The same migration pattern applies to each: search the code base for the removed name, apply the replacement in the table, then run the suite on the new line.

Worked replacements

Four changes cause most upgrade failures in real projects: customization hooks, import syntax, TypeScript syntax and shell-based child processes.

module.register() becomes module.registerHooks()

module.register() registered asynchronous hooks that ran in a separate thread; it is Deprecated (Stability 0), became documentation-only deprecated in v25.9.0 and warns at run time since v26.0.0. module.registerHooks() is synchronous, runs in the same thread, works for every module type, and is Stability 1.2 (Release candidate) since v25.4.0 and v24.13.1; it was added in v23.5.0 and v22.15.0.

The old and the new registration
// hooks-old.mjs
import { register } from 'node:module';
register('./loader-hooks.mjs', import.meta.url);
console.log('registered with module.register()');

// hooks-new.mjs
import { registerHooks } from 'node:module';

registerHooks({
  resolve(specifier, context, nextResolve) {
    return nextResolve(specifier, context);
  },
});
console.log('registered with module.registerHooks()');
Result: only Node.js 26.10.0 warns about the old form
registered with module.register()
(node:97226) [DEP0205] DeprecationWarning: `module.register()` is deprecated. Use `module.registerHooks()` instead.
registered with module.registerHooks()

The warning appeared only on 26; both forms ran silently on 24.21.0 and 22.23.3. The details of writing hooks are in ESM & CommonJS Interoperability.

Import assertions become import attributes

The assert { type: 'json' } form was standardised as with { type: 'json' }; Node.js dropped support for assertions in 22.0.0 (it had accepted attributes since v21.0.0, v20.10.0 and v18.20.0). Every line from 22 to 26 reports a SyntaxError for the old keyword, and the codemod shown above does the rewrite.

The same file with the old and the new keyword
// assert.mjs: SyntaxError: Unexpected identifier 'assert' on 22, 24 and 26
import data from './data.json' assert { type: 'json' };

// with.mjs: prints "bookshelf" on 22, 24 and 26
import data from './data.json' with { type: 'json' };
console.log(data.name);

Enums and other non-erasable TypeScript syntax

Type stripping runs .ts files directly by erasing types; it was added in v22.6.0, is on by default since v23.6.0 and v22.18.0, prints no experimental warning since v24.3.0 and v22.18.0, and is Stable since v25.2.0 and v24.12.0. The flag that used to transform enums, namespaces and parameter properties, --experimental-transform-types, was removed in v26.0.0. On 26 there is no flag to fall back on, so the code must use erasable syntax. Run on each line with the default settings, an enum file fails with ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX ("TypeScript enum is not supported in strip-only mode"), and with --experimental-transform-types it ran on 22.23.3 and 24.21.0 (with an ExperimentalWarning) but is "bad option" on 26.10.0.

An enum and its erasable equivalent
// ts.ts: fails in strip-only mode on 22, 24 and 26
enum Color { Red, Green }
console.log(Color.Green);

// ts-erasable.ts: prints 1 on 22.23.3, 24.21.0 and 26.10.0
const Color = { Red: 0, Green: 1 } as const;
type Color = (typeof Color)[keyof typeof Color];
const c: Color = Color.Green;
console.log(c);

More on what strip-only mode accepts, and the tsconfig options that enforce it, is in TypeScript in Node.js.

An args array with a shell

Passing an arguments array together with shell: true does not escape the arguments; they are only joined with spaces, which is a shell-injection hazard (DEP0190). It prints a runtime warning from v24.0.0. The safe form is to keep the array and drop the shell.

The deprecated and the safe call
import { execFileSync } from 'node:child_process';

// Deprecated since 24.0.0 (DEP0190): an args array together with shell: true
console.log(execFileSync('echo', ['hello', 'world'], { shell: true, encoding: 'utf8' }).trim());

// Replacement: no shell, arguments stay separate array items
console.log(execFileSync('echo', ['hello', 'world'], { encoding: 'utf8' }).trim());

Both lines printed hello world on all three lines, and [DEP0190] DeprecationWarning: Passing args to a child process with shell option true can lead to security vulnerabilities, as the arguments are not escaped, only concatenated. appeared only on 24.21.0 and 26.10.0. Source: Node.js API: DEP0190

On Windows, .bat and .cmd files cannot be launched with execFile() because they need a terminal; the child-process documentation lists exec(), or spawning cmd.exe explicitly, as the ways to run them, and marks spawn() with the shell option as not recommended because of DEP0190. See Child Processes for the argument-array rules.

Highlights per major

Each table lists what changed in the release, in the order an upgrade is most likely to hit it. Version numbers for follow-up changes come from the history entries of the API documentation, and all release dates come from the release posts.

Node.js 22 (released 2024-04-24, LTS 2024-10-29)

Table 7. Notable changes in Node.js 22 and its later 22.x minors
Area Change Version

Engine

V8 12.4, Maglev compiler enabled on supported architectures.

22.0.0

Modules

require() of a synchronous ES module graph arrived behind --experimental-require-module; the flag was dropped in v22.12.0 and the experimental warning in v22.13.0.

22.0.0, 22.12.0

Modules

Import assertions (assert) dropped in favour of import attributes (with).

22.0.0

Modules

Syntax detection enabled by default: an ambiguous .js file with import/export is treated as an ES module.

22.7.0

Hooks

module.registerHooks() added (synchronous, in-thread); module.register() is deprecated later, in 25.9.0 and 26.0.0.

22.15.0

CLI

node --watch marked Stable; node --run <script> added (it traverses up to find package.json).

22.0.0

Web platform

The WebSocket client enabled by default.

22.0.0

Streams

The default highWaterMark raised to 65536 bytes (measured on 22.23.3, 24.21.0 and 26.10.0 with new Readable().readableHighWaterMark).

22.0.0

File system

fs.glob() and fs.globSync() added; marked stable in v22.17.0 and v24.0.0.

22.0.0, 22.17.0

Data

node:sqlite added (behind a flag); the flag was removed in v22.13.0.

22.5.0, 22.13.0

TypeScript

Type stripping added behind a flag (--experimental-strip-types), enabled by default and without the experimental warning in v22.18.0.

22.6.0, 22.18.0

Configuration

--env-file and friends no longer experimental from v22.21.0 (also v24.10.0).

22.21.0

Node.js 23 (released 2024-10-16, end-of-life 2025-06-01)

Table 8. Notable changes in Node.js 23
Area Change Version

Modules

require(esm) enabled by default (the --experimental-require-module flag removal also reached 22.12.0 and 20.19.0). The experimental warning was removed in v23.5.0 (22.13.0, 20.19.0).

23.0.0, 23.5.0

Platform

Support for 32-bit Windows removed.

23.0.0

CLI

node --run stabilised; the test runner accepts glob patterns for coverage files.

23.0.0

Removals

util.isBoolean() and the other util.is*() helpers, util.log(), process.assert() and --trace-atomics-wait moved to end-of-life.

23.0.0

Data

node:sqlite no longer needs --experimental-sqlite (it is still experimental there).

23.4.0

Hooks

module.registerHooks() added.

23.5.0

TypeScript

Type stripping enabled by default.

23.6.0

Deprecation

node:punycode becomes an application deprecation (DEP0040).

23.7.0

Node.js 24 "Krypton" (released 2025-05-06, LTS 2025-10-28)

Table 9. Notable changes in Node.js 24
Area Change Version

Engine

V8 13.6: Float16Array, explicit resource management (using), RegExp.escape(), Error.isError() and WebAssembly Memory64. A probe on the three lines showed using as a SyntaxError on 22, working on 24 and 26.

24.0.0

npm

npm 11 bundled (24.21.0 ships npm 11.19.0).

24.0.0

Context

AsyncLocalStorage uses AsyncContextFrame by default.

24.0.0

Web platform

URLPattern exposed as a global (Stability 1, Experimental; absent on 22).

24.0.0

Security

The permission-model flag is --permission, replacing --experimental-permission.

24.0.0

Testing

The test runner waits for subtests automatically, with no need to await them.

24.0.0

Networking

Undici 7.

24.0.0

Crypto

OpenSSL 3.5 with security level 2: RSA, DSA and DH keys shorter than 2048 bits, ECC keys shorter than 224 bits and RC4 cipher suites are prohibited. crypto.argon2() and argon2Sync() added in v24.7.0.

24.0.0, 24.7.0

Platform

No pre-built binaries for 32-bit ARMv7 Linux; macOS binaries require macOS 13.5 or later; building from source needs GCC 12.2 on Linux or AIX and Xcode 16.1 on macOS.

24.0.0

Deprecations

Runtime: url.parse() (application scope), SlowBuffer, REPL and zlib classes without new, DEP0190. Removed: tls.createSecurePair().

24.0.0

Modules

import.meta.main added (also v22.18.0).

24.2.0

TypeScript

Type stripping Stable. require(esm) is documented as no longer experimental in v24.15.0 (v25.4.0 on the Current line).

24.12.0, 24.15.0

Node.js 25 (released 2025-10-15, end-of-life 2026-06-01)

Table 10. Notable changes in Node.js 25
Area Change Version

Engine

V8 14.1: faster JSON.stringify() and built-in Uint8Array base64 and hex conversion (not available on 22 or 24: Uint8Array.fromBase64 was a function only on 26.10.0 in the probe below).

25.0.0

Security

--allow-net added to the permission model (Stability 1.1, Active development, so the network scope is not Stable even though the rest of the model is).

25.0.0

Web platform

Web Storage unflagged; ErrorEvent added as a global (typeof ErrorEvent is 'function' on 26.10.0 and 'undefined' on 24.21.0 and 22.23.3).

25.0.0

Distribution

Corepack is no longer distributed with Node.js; install it from npm if you need it.

25.0.0

Removals

SlowBuffer, fs.rmdir() with recursive, fs.F_OK and the other getters, assert.CallTracker, multi-argument assert.fail(), process multipleResolves, FileHandle closing on garbage collection and several dgram, tls and dns leftovers moved to end-of-life; ecdh.setPublicKey(), SHAKE without outputLength and ServerResponse.writeHeader() became runtime deprecations.

25.0.0

Compile cache

A portable compile cache and WebAssembly JSPI.

25.0.0

Modules

module.registerHooks() reaches Release candidate (also v24.13.1); require(esm) no longer experimental (v25.4.0).

25.4.0

Single executables

--build-sea=config added (Stability 1.1, Active development).

25.5.0

Data

node:sqlite becomes a Release candidate; Duplex.toWeb() type option documentation-deprecated.

25.7.0

TypeScript

Type stripping Stable (also v24.12.0).

25.2.0

Node.js 26 (released 2026-05-05, LTS 2026-10-28)

Table 11. Notable changes in Node.js 26
Area Change Version

Language

The Temporal API is enabled by default (typeof Temporal is 'object' on 26.10.0 and 'undefined' on 24.21.0 and 22.23.3); V8 14.6 adds Map.prototype.getOrInsert(), Iterator.concat() and related upsert and sequencing features.

26.0.0

Networking

Undici 8.

26.0.0

Removals

--experimental-transform-types, ServerResponse.prototype.writeHeader(), stream* modules, short GCM tags (DEP0182).

26.0.0

Runtime deprecations

module.register() (DEP0205), CryptoKey passed to node:crypto (DEP0203, DEP0204), Duplex.toWeb() type option (DEP0201).

26.0.0

Buffers

Buffer.poolSize default raised from 8192 to 65536 (also v24.18.0; observed 8192 on 22.23.3).

26.3.0

CLI

--disable-warning becomes Stable (also v24.20.0); node --run with no argument lists the scripts (observed: a "requires an argument" error on 22.23.3 and 24.21.0).

26.7.0, 26.9.0

Deprecations

DEP0206 (Hmac digest twice), DEP0208 (_listen2) and DEP0209 (AbortSignal as a disposer), all documentation-only.

26.2.0, 26.8.2

Networking

The default autoSelectFamilyAttemptTimeout measured 500 ms on 26.10.0 and 250 ms on 24.21.0 and 22.23.3 (observed with net.getDefaultAutoSelectFamilyAttemptTimeout()).

observed

Detecting the line at run time

Feature detection beats version sniffing when a library has to run on several lines, and a short probe also documents what your own upgrade depends on. This script checks the Release-candidate, Stable and Experimental APIs that differ across 22, 24 and 26:

A feature probe for the differences between 22, 24 and 26
import { createRequire } from 'node:module';
import * as sqlite from 'node:sqlite';

const require = createRequire(import.meta.url);
const crypto = require('node:crypto');

const checks = {
  'require(esm) enabled': process.features.require_module,
  'Temporal': typeof globalThis.Temporal,
  'URLPattern': typeof globalThis.URLPattern,
  'Error.isError': typeof Error.isError,
  'node:sqlite DatabaseSync': typeof sqlite.DatabaseSync,
  'crypto.argon2Sync': typeof crypto.argon2Sync,
  'Uint8Array.fromBase64': typeof Uint8Array.fromBase64,
  'module.registerHooks': typeof require('node:module').registerHooks,
  'fs.glob': typeof require('node:fs').glob,
  'localStorage (no flag)': Object.hasOwn(globalThis, 'localStorage') ? 'present' : 'absent',
};
console.log(process.version, 'V8', process.versions.v8.split('.').slice(0, 2).join('.'));
console.table(checks);
Table 12. Probe results (node --no-warnings features.mjs)
Check 22.23.3 (V8 12.4) 24.21.0 (V8 13.6) 26.10.0 (V8 14.6)

process.features.require_module

true

true

true

Temporal

undefined

undefined

object

URLPattern

undefined

function

function

Error.isError

undefined

function

function

node:sqlite DatabaseSync

function

function

function

crypto.argon2Sync

undefined

function

function

Uint8Array.fromBase64

undefined

undefined

function

module.registerHooks

function

function

function

fs.glob

function

function

function

localStorage own property

absent

absent

present

On 26 the localStorage property exists, but reading it without --localstorage-file=<file> prints an ExperimentalWarning saying that localStorage is not available because the flag was not provided (observed on 26.10.0); the flag is Stability 1.2, Release candidate. A similar probe of language features gave this result: RegExp.escape, Float16Array and Promise.try are functions on 24 and 26 and undefined on 22; Iterator.concat(), Map.prototype.getOrInsert() and Uint8Array.prototype.toBase64() are functions only on 26; Set.prototype.union and Symbol.dispose exist on all three lines; and a using declaration is a SyntaxError on 22.23.3.

Version-dependent behaviour to know about

The collected notes below were verified against the official pages for the stability level and "added in" version, and measured on the three lines where a behaviour is better shown than described. Items marked "observed" describe what a given patch release did and are not a promise.

Table 13. Stability levels and version caveats worth checking before an upgrade
Topic State on 2026-10-04 Where it differs

process.nextTick()

Stability 3, Legacy: "use `queueMicrotask()`".

The Legacy marking dates from v22.7.0 and v20.18.0, so it applies on 22, 24 and 26; it was not a 24 or 25 change.

Permission model

Stable since 20.0.0 for the filesystem, child-process and worker scopes; flag renamed to --permission in 24.0.0.

--allow-net (added v25.0.0) and --allow-fs-vfs (added v26.9.0) are Stability 1.1, Active development; do not describe the whole model as Stable.

node:sqlite

Stability 1.2, Release candidate since v25.7.0.

Added in v22.5.0 behind a flag, unflagged in v23.4.0 and v22.13.0.

require(esm)

Stability 2, Stable; no flag since v22.12.0 / v23.0.0 / v20.19.0.

"No longer experimental" in v25.4.0 and v24.15.0; the 22.x documentation still marks it Release candidate (Stability 1.2).

Type stripping

Stable on 24 (v24.12.0) and 26 (v25.2.0).

On 22.23.3 it is on by default but the documentation has no stable level; --no-strip-types is "bad option" on 22.23.3 and accepted on 24 and 26 (observed).

SlowBuffer

Removed in 25.0.0.

Still exported on 24, where it prints DEP0030 (observed on 24.21.0).

URLPattern

Stability 1, Experimental; global since v24.0.0.

Absent on 22. On 24.21.0 and 26.10.0 it ran without an ExperimentalWarning (observed).

Web Storage

Stability 1.2, Release candidate; enabled by default since v25.0.0.

On 26 it needs --localstorage-file to be usable (observed warning); on 24 and 22 the global was absent without a flag.

Config file

--experimental-config-file (reads node.config.json) is Stability 1.2, Release candidate since v26.7.0; added in v23.10.0 and v22.16.0.

The 22.x documentation marks it Stability 1.0, Early development; keep it out of projects that must run on 22.

Single executable applications

Stability 1.1, Active development.

--build-sea=config needs 25.5.0 or later; on 22 and 24 the documented flow is --experimental-sea-config to write a blob, then injecting it with postject.

Environment files

--env-file, --env-file-if-exists, process.loadEnvFile(), util.parseEnv() no longer experimental.

From v24.10.0 and v22.21.0.

util.styleText()

Stable since v23.5.0 and v22.13.0.

Hex colours are documented as added in 26.1.0, but a hex colour already worked on 24.21.0 (observed).

The platform keeps adding experimental surface while it stabilises older features, which is the other half of upgrade planning: on Node.js 26, node:ffi (v26.1.0), the --allow-fs-vfs virtual file system, the iterable-streams module stream/iter (flag-gated), the Worker web-worker global (--experimental-web-worker, rejected on 22 and 24) and mock.module() in the test runner are Experimental or in early development, so keep them out of code that has to run on a Maintenance line. Pages in this section mark the stability level of every API they use; start from the stability index described in Globals & Web Platform APIs.

What changed recently

Table 14. Dated changes that affect upgrade planning (2025 to 2026)
Date or version Change

2025-10-15

Node.js 25.0.0: Corepack no longer distributed, --allow-net, Web Storage on by default, many end-of-life removals.

2025-12-09

npm classic tokens permanently revoked (GitHub changelog); publishing uses granular tokens or trusted publishing (OIDC).

2026-03-10

The new release schedule is announced: one major per year from Node.js 27, every line becomes LTS, alpha channel from October.

2026-04-30

Node.js 20 reaches end-of-life.

2026-05-05

Node.js 26.0.0: Temporal, V8 14.6, Undici 8, --experimental-transform-types removed, module.register() runtime-deprecated.

2026-06-01

Node.js 25 reaches end-of-life.

2026-07-08

npm 12.0.0: dependency install scripts blocked by default, npm shrinkwrap and npm adduser removed. Not bundled with any Node.js line.

2026-10-20

Node.js 24 moves to Maintenance.

2026-10-28

Node.js 26 becomes Active LTS; the Node.js 27 alpha channel opens.

2027-04-22

Node.js 27.0.0 is scheduled, the first line of the annual model.

2027-04-30

Node.js 22 reaches end-of-life.

The practical consequence is a tight window: with Node.js 22 ending on 2027-04-30 and Node.js 24 moving to Maintenance on 2026-10-20, a service on 22 should plan the move to 24 or 26 now, and a service on 24 should plan the move to 26 during the first half of 2027.

Where the books are outdated

Both reference books were published in 2025 and were written against Node.js 20 to 22 era behaviour. Everything below was verified against the official documentation on 2026-10-03 and re-checked where this page says so. The statements are summarised in the author’s own words, with the current approach next to them, and the same notes appear on the page of the concept they affect. The books are Efficient Node.js by Samer Buna and Node.js Projects by Jonathan Wexler, both published by O’Reilly Media.

Table 15. Versions, tooling and built-ins that replaced packages
Area What the older material assumes Current approach

Version baseline

Node.js 18 or 20 as the minimum, and Docker images based on node:20 running as root.

Node.js 18 and 20 are end-of-life. Use a node:26-slim or node:24-slim image with a non-root user (see Docker & Containers).

Corepack

Enabling Yarn or pnpm through Corepack as part of the installation.

Corepack is not distributed since Node.js 25.0.0; install it from npm or use the package manager directly (see Installing Node.js).

Restarting on change

nodemon.

node --watch (Stable since 22.0.0 and 23).

Script runners

npm-run-all and similar.

node --run <script> (added v22.0.0, stable in 23; lists scripts without an argument from v26.9.0).

Environment files

The dotenv package.

--env-file, --env-file-if-exists, process.loadEnvFile() and util.parseEnv() (no longer experimental in v24.10.0 and v22.21.0); see CLI Options, Environment & Config File.

Prompts and colours

prompt or prompt-sync, chalk, kleur, ansi-colors, argument-parsing packages.

node:readline/promises, util.styleText(), util.parseArgs().

Local database

The sqlite3 or sqlite packages for prototypes.

node:sqlite, a Release candidate since v25.7.0.

Password hashing

bcrypt.

crypto.scrypt() or crypto.argon2() (added v24.7.0).

HTTP and WebSocket clients

node-fetch, axios, ws for client use.

Global fetch (stable since 21) and the global WebSocket client (enabled in 22.0.0).

Globbing

glob packages.

fs.glob() (stable since v22.17.0 and v24.0.0).

Test libraries

A test framework for snapshots and module mocks.

The node:test snapshot support and mock.module() (the latter Early development).

Table 16. Modules, TypeScript and the permission model
Area What the older material assumes Current approach

Module detection

A "type": "module" field or .mjs extension for every ES module.

Syntax detection is on by default since v22.7.0 for ambiguous files; "type" remains the explicit, recommended setting.

require(esm)

ES modules cannot be loaded from CommonJS.

require() loads a synchronous ES module graph without a flag on every supported line (v22.12.0+); it throws ERR_REQUIRE_ASYNC_MODULE for top-level await.

Import assertions

assert { type: 'json' }.

with { type: 'json' }; assertions were dropped in 22.0.0.

Customization hooks

module.register().

module.registerHooks(); register() warns since v26.0.0 (DEP0205).

import.meta

dirname and filename workarounds.

import.meta.dirname and import.meta.filename are stable; import.meta.main exists since v24.2.0 and v22.18.0.

TypeScript

A compile step with tsc before running; the idea that tsc refuses to emit on type errors.

Node runs erasable-syntax .ts files directly (Stable since v24.12.0 and v25.2.0); --experimental-transform-types was removed in v26.0.0; use tsc --noEmit for checking. tsc emits despite type errors unless noEmitOnError is set.

Permission model

Mentioned only as an upcoming feature.

Stable for filesystem, child-process and worker scopes (--permission, --allow-fs-read, --allow-fs-write, --allow-child-process, --allow-worker); --allow-net is Active development.

Table 17. npm, child processes and other APIs
Area What the older material assumes Current approach

Production installs

--production, or a prune followed by install before tests.

npm ci and --omit=dev; --production is deprecated.

Publishing credentials

Classic npm tokens.

Classic tokens were revoked on 2025-12-09; use granular tokens or trusted publishing with provenance (trusted publishers).

npm 12

Install scripts always run.

npm 12.0.0 blocks dependency scripts unless allowed with allowScripts, and removes npm shrinkwrap and npm adduser; min-release-age (added in npm 11.10.0) adds a dependency cooldown.

Shell and arguments

spawn() or execFile() with an args array and shell: true.

Runtime-deprecated since v24.0.0 (DEP0190); keep the array and drop the shell. Windows .bat and .cmd need exec() or an explicit cmd.exe.

Microtasks

process.nextTick() as the default way to defer work.

Legacy since v22.7.0; prefer queueMicrotask().

URLs

url.parse().

The WHATWG URL; url.parse() warns in application code since v24.0.0.

Removed members

fs.rmdir(path, { recursive: true }), SlowBuffer, fs.F_OK getters, stream* modules, ServerResponse.writeHeader().

Removed in v25.0.0 and v26.0.0; use fs.rm(), Buffer.allocUnsafeSlow(), fs.constants, node:stream and writeHead().

Release model

Even-numbered lines are the production lines.

From Node.js 27 every line is LTS and there is one major per year.

Table 18. Ecosystem facts that changed since 2025
Topic Current state

Express and Fastify

Express 5 is the current major (npm latest 5.2.1 on 2026-10-03). Fastify 4 reached end-of-life on 2025-06-30 (Fastify’s LTS table); plugin majors must match Fastify 5.

create-react-app

Deprecated by the React team (react.dev announcement).

Job queues

The Bull repository points to BullMQ for the next major version of the library; BullMQ is the maintained choice for new projects.

Docker Compose

The version: top-level element is obsolete in the Compose Specification.

Gemini SDK and model

The @google/generative-ai package is legacy and replaced by @google/genai; the gemini-2.0-flash model was scheduled for shutdown on 2026-06-01 (Google’s deprecations table), so code from the book’s chapter on LLMs no longer runs as written. Put keys in a header, never in a query string, and prefer structured output over stripping JSON fences from free text.

MongoDB server

MongoDB 5.0 reached end-of-life on 2024-10-31 and 6.0 on 2025-07-31; run a supported release (7.0, 8.0 or later) for new work.

Adding APT repositories

apt-key is deprecated and was last shipped in Debian 11 and Ubuntu 22.04. Store a repository’s key in its own file under /etc/apt/keyrings and reference it with signed-by= in the source entry.

Editors

GitHub sunset the Atom editor on 2022-12-15 and archived its repositories; use a maintained editor such as VS Code.

HTTP/2 server push

Chrome 106 disabled server push by default and it is effectively dead on the web; send 103 Early Hints or Link: rel=preload instead (see HTTPS, TLS & HTTP/2).

Insecure practices that the projects should not copy

Some sample projects in Node.js Projects (Jonathan Wexler) are teaching code, not production code, and a reader who copies them inherits their weaknesses. The safe form for each:

Table 19. Insecure practices and the safe form
Area Weakness in the sample projects Safe form

Stored secrets

A password manager that keeps the managed passwords readable.

Derive a key with scrypt or Argon2id and encrypt each secret with AES-256-GCM, storing the IV and auth tag with the ciphertext (see Crypto, Passwords & Secrets).

Password hashing

A low-iteration, synchronous PBKDF2 whose hashes are compared with ===.

Use at least 210,000 iterations for PBKDF2-HMAC-SHA512 (OWASP), or better Argon2id or scrypt, the asynchronous API and crypto.timingSafeEqual().

Configuration

Hard-coded secrets and JWT secrets that fall back to a fixed string.

Fail fast at start-up when a secret is missing, and read it from process.env through --env-file.

Tokens

JWTs issued without an expiry and verified without pinned algorithms; session cookies without secure, httpOnly and sameSite.

Set expiresIn, pass algorithms when verifying, and set all three cookie attributes (see Authentication & Sessions).

Request protection

No CSRF protection, no rate limiting, and an email address placed in a verification URL.

Add CSRF defences and rate limits, and verify email with a random single-use token.

Scraping and templates

A scraper that spoofs its user agent and ignores robots.txt; a vulnerable version of the ejs template engine (CVE-2022-29078).

Respect robots.txt and site terms, identify your client, and keep template engines up to date (see Email, Feeds & Web Scraping).

Cloud database access

A MongoDB Atlas network access list open to every address.

Restrict access to the addresses that need it, and use credentials from the environment.

LLM prompts

User input concatenated into a single prompt string, and the model’s reply used as if it were trusted data.

Prompts are injectable: keep instructions in the system role and user text in the user role, treat model output as untrusted input, and request structured output that you validate before use (see Integrating LLM APIs).

Errors in the books worth knowing

Typos and slips are normal in technical books, and the page that covers each concept already shows the right form; the ones below are collected so that a reader following either book can check them in one place. The correct form is given, not the passage.

Table 20. Corrections for Efficient Node.js (Samer Buna)
Topic Correct form

Promise helper module

The module is node:stream/promises (singular stream).

Awaiting several promises

await [p1, p2] does not wait for both; use Promise.all(), Promise.allSettled() or another combinator.

Per-request child processes

A child forked per request must exit, or each request leaks a process; a request or response object cannot be sent to a worker (it is not serialisable).

Client IP hashing

An address-hashing sketch for sticky balancing must handle IPv6 as well as IPv4.

ES modules

Use export and export default in ES modules; exports and module.exports are CommonJS.

NODE_PATH

The separator is : on Unix and ; on Windows, not a comma, and the variable applies to CommonJS only.

assert.doesNotMatch()

It passes when the string does not match the pattern and throws when it does.

ENOTFOUND

A DNS lookup failure, not "entity not found".

Pausing a stream

Removing 'data' listeners does not pause a stream; call pause() or use pipeline().

Backpressure

Large generators must respect backpressure by honouring the result of write() and waiting for 'drain'.

Encryption

aes-256-ctr has no integrity protection; use aes-256-gcm and check the authentication tag.

Promise chains

A ; ends the statement, so a .finally() on the next line after then(…​); is a SyntaxError; chain .finally() without the semicolon.

fs.readFile() size limit

readFile() rejects files over 2 GiB with ERR_FS_FILE_TOO_LARGE; this is a fixed internal I/O limit that no option or heap flag raises, so stream large files with fs.createReadStream().

Orders test fixtures

The orders in the testing example have no ids, so the tests pass by accident; give every fixture a unique id so a lookup, update or delete can match only the intended record.

Table 21. Corrections for Node.js Projects (Jonathan Wexler)
Topic Correct form

promisify example

The readline interface used in the example must be created before it is used.

npm start

It needs a start script in package.json; without one npm start runs node server.js only if that file exists.

Not-found handlers

A Fastify setNotFoundHandler should reply with 404, not 500, and reply.send() must be called once.

Sequelize sync()

sync({ force: true }) drops and recreates tables; it is not change detection (use migrations).

Schedulers and queues

Use the documented node-schedule rule keys, declare every queue you use, and treat Redis Pub/Sub as at-most-once messaging, not a durable queue; acknowledge a RabbitMQ message only after the work is done.

Blockchain project

A transaction needs its own class, and a proof-of-work nonce must be bound to the block it signs.

LLM errors

An error object returned by the API is not an answer; check the status and the error field first.

MongoDB connection strings

mongodb://localhost can fail where localhost resolves to IPv6 first; use 127.0.0.1 or [::1] explicitly.

Topics the books do not cover

Where the books say little or nothing, the official documentation supplies it, and this section has a page for each: the real libuv event-loop phases and queueMicrotask() versus process.nextTick() versus setImmediate(), AbortSignal, AsyncLocalStorage and using (Event Loop, Async Context & Cancellation); "exports" and "imports", conditional exports, dual packages and require(esm) (package.json: exports & imports); Web Streams, Buffer, fs.glob() and HTTP/2 (Web Streams & Interop, HTTPS, TLS & HTTP/2); worker_threads (Worker Threads); snapshots, module mocks and coverage (Mocking, Snapshots & Coverage); profilers, heap snapshots and diagnostic reports (Profiling & Performance); and the official threat model, the permission model and supply-chain hygiene (Security Best Practices, Permission Model).

References