What Is New in Node.js 22
| Category | Highlights |
|---|---|
| New Features | Built-in WebSocket client enabled by default; require() for synchronous ESM graphs; node:sqlite experimental module (v22.5+); node --run CLI shortcut; fs.glob / fs.globSync; TypeScript stripping support (v22.6+); process.ref() / process.unref() (v22.14+) |
| Improvements | V8 upgraded to 12.4 with Maglev compiler; streams default highWaterMark bumped from 16 KiB to 64 KiB; --watch mode marked stable; AbortSignal creation performance boost; atob / btoa now use simdutf; updated root certificates (NSS 3.107 in v22.14); test runner gains suite(), file snapshots, waitFor() |
| Bug Fixes | TLS callback exceptions properly routed through error handlers (CVE-2026-21637); unsafe buffer zero-fill toggle removed (CVE-2025-55131); symlink APIs require full read/write permissions (CVE-2025-55130); stack overflow exceptions rethrown correctly in async_hooks (CVE-2025-59466) |
| Breaking Changes | Import assertions (non-attribute syntax) dropped; streams highWaterMark default changed; NODE_MODULE_VERSION bumped to 127; console.assert() treats non-strings as separate argument; crypto.createCipher / createDecipher moved to end-of-life; .bat / .cmd direct spawn blocked (security) |
| Deprecations | All legacy util.is*() type-check helpers runtime-deprecated; util.log, util._extend runtime-deprecated; crypto.Hash and crypto.Hmac constructors runtime-deprecated; fs.Stats public constructor runtime-deprecated; --experimental-policy flag deprecated; repl.builtinModules deprecated (v22.16+) |
Does Node.js 22 finally let you require() ESM modules?
Yes -- Node.js 22 introduces experimental support for require()-ing synchronous ESM graphs, one of the most requested features in the ecosystem for years. Before this change, CommonJS code could not directly consume ESM modules, forcing teams to either refactor to full ESM or rely on dynamic import() workarounds. Now, if an ESM graph contains no top-level await, it can be loaded synchronously via require():
// Works in Node.js 22 -- consuming an ESM module from CJS
const { helper } = require('./esm-helper.mjs');
console.log(helper());
In practice, this is a significant migration accelerator. Many teams have mixed CJS/ESM codebases where one package is stuck on CJS due to a single deep dependency. This change unlocks incremental migration rather than big-bang rewrites. Watch out for modules that do use top-level await -- those will still throw at require() time, and the error message is now more actionable with location info for unsettled TLA.
On a related note, the ESM import assertions syntax (the old assert { type: "json" } form) was dropped entirely in 22.0.0. The replacement is import attributes (with { type: "json" }). Any code using the old assertion syntax will break. Update your bundler configs and source files before upgrading.
What does the built-in SQLite module in Node.js 22 mean for your stack?
Starting with v22.5.0, Node.js ships a native node:sqlite module -- no npm install required. The API is synchronous by design, built around DatabaseSync and StatementSync, making it straightforward to use without dealing with callbacks or promise chains for simple operations:
// Available from v22.5+ with --experimental-sqlite
// Stable-ish from v22.16+ with continued API additions
import { DatabaseSync } from 'node:sqlite';
const db = new DatabaseSync('./app.db');
db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)');
const insert = db.prepare('INSERT INTO users (name) VALUES (?)');
insert.run('Alice');
const all = db.prepare('SELECT * FROM users');
console.log(all.all()); // [{ id: 1, name: 'Alice' }]
The module has matured substantially across the v22.x lifecycle. By v22.12 it gained session-based change tracking. By v22.14 it supports TypedArray and DataView as parameter bindings. By v22.16 it added StatementSync.prototype.columns(), timeout options, and the location() method for inspecting database file paths. Most teams should still reach for better-sqlite3 in production when they need advanced features or custom SQLite builds, but node:sqlite is already excellent for testing, prototyping, CLI tools, and embedding lightweight storage in server-side scripts.
How does the V8 Maglev compiler in Node.js 22 affect application performance?
Node.js 22 ships with V8 12.4 and enables the Maglev compiler by default on supported architectures (x64 and arm64). Maglev sits between Sparkplug (fast, unoptimized JIT) and Turbofan (heavily optimized JIT) in V8's compilation pipeline, providing a middle tier that compiles faster than Turbofan while producing significantly better code than Sparkplug. For most server-side workloads this translates to improved throughput on short-to-medium-lived hot functions without the deoptimization overhead associated with Turbofan speculation.
The same V8 update also brings language feature additions: Array.fromAsync, WebAssembly Garbage Collection, new Set methods (union, intersection, difference, etc.), and iterator helpers. These are available without flags. This matters if you are maintaining libraries that need to support both Node 20 and 22 -- avoid using these new builtins without feature detection or transpilation in shared code.
Most teams will see the performance gains automatically. The one thing to watch out for: memory profiling tools and native addons that hook into V8 internals may need updates. The NODE_MODULE_VERSION bump to 127 means prebuilt native addons from Node 20 are not binary-compatible and must be rebuilt or updated.
Is TypeScript support in Node.js 22 production-ready?
Node.js 22 introduces experimental TypeScript support via type-stripping -- it can execute .ts files directly by removing type annotations before handing the code to V8. No compilation step, no ts-node, no tsx dependency required. This landed in v22.6.0 and has been iterating across minor releases:
# Run TypeScript files directly (experimental from v22.6+)
node --experimental-strip-types app.ts
# With type transformation support (enums, decorators)
node --experimental-transform-types app.ts
# Evaluate TypeScript from stdin (v22.14+)
echo "const x: number = 42; console.log(x);" | node --input-type=ts --experimental-strip-types
The implementation is intentionally minimal: it strips types but does not type-check. Think of it as a convenience feature for scripts, tooling, and developer experience -- not a replacement for a proper build pipeline in production. By v22.14, the error code ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX was added to make it clearer when you hit a TypeScript feature that requires transformation rather than just stripping. Most teams should treat this as a strong signal that Node.js is headed toward first-class TypeScript support, but for production applications with strict correctness requirements, keep tsc or esbuild in the loop.
What breaking changes and deprecations in Node.js 22 require action before upgrading?
Node.js 22 carries a meaningful deprecation sweep that will generate console warnings at runtime. The most wide-reaching: all of the old util.is*() type-checking helpers are now runtime-deprecated. If your codebase or its dependencies call util.isArray(), util.isString(), util.isObject(), or any of the other util.is* functions, you will see deprecation warnings in logs. Replace them with standard equivalents like Array.isArray(), typeof checks, or instanceof.
Other areas that need attention before upgrading:
- Import assertions -- replace
assert { type: "json" }withwith { type: "json" }in all ESM import statements. - crypto.createCipher / createDecipher -- these reached end-of-life in 22.0.0 and will throw. Migrate to
crypto.createCipherivandcrypto.createDecipherivwith explicit IVs. - crypto.Hash and crypto.Hmac constructors -- calling
new crypto.Hash()directly emits a runtime deprecation. Usecrypto.createHash()instead. - Streams highWaterMark change -- the default jumped from 16 KiB to 64 KiB. In memory-constrained environments (containers with tight limits, edge functions) this could increase baseline memory usage. Profile before deploying.
- Native addon compatibility -- NODE_MODULE_VERSION is now 127. All binary addons must be rebuilt against Node 22 headers.
The security-focused breaking change worth noting separately: Node.js 22.0.0 (and patched in later releases) blocks direct spawning of .bat and .cmd files on Windows via child_process to address CVE-2024-27980. Code that previously passed a .bat path directly to spawn() must now explicitly invoke cmd.exe with the script as an argument.
Frequently Asked Questions about Node.js 22
Do native addons compiled for Node.js 20 work on Node.js 22?
No. The NODE_MODULE_VERSION was bumped to 127 in Node.js 22, which means binary addons built against Node 20 are not ABI-compatible and must be recompiled or updated to a release that targets Node 22.
Is the node:sqlite module stable in Node.js 22?
The module remains experimental across the v22.x line, meaning its API can still change in minor releases. It is actively evolving -- each minor release since v22.5 has added new methods and fixed bugs -- so it is well-suited for non-critical uses like tooling and testing, but teams should wait for a stable graduation before relying on it in high-stakes production workloads.
How do I use require() with ESM modules in Node.js 22?
You can call require() on any synchronous ESM graph -- meaning an ESM file and its entire dependency tree must not contain top-level await. Simply call require('./your-esm-file.mjs') from CJS code. If any file in the graph uses top-level await, Node.js will throw an error at load time. This feature is on by default in v22 with no flag needed.
What replaced the deprecated util.is*() functions in Node.js 22?
Replace util.isArray() with Array.isArray(), replace util.isString() and similar primitive checks with typeof, and replace util.isDate() or util.isRegExp() with instanceof checks. These are the standard equivalents the team has documented in the migration paths added to the Node.js docs alongside the deprecations.
Does the streams highWaterMark change in Node.js 22 affect existing code?
The default highWaterMark increased from 16 KiB to 64 KiB, which improves throughput for most streaming workloads by reducing event loop overhead but increases baseline memory usage per stream. Applications running in memory-constrained environments should benchmark after upgrading and pass an explicit highWaterMark option to stream constructors if needed.
Is the TypeScript stripping feature in Node.js 22 a replacement for tsc?
No. The experimental --experimental-strip-types flag removes type annotations so Node.js can execute TypeScript files directly, but it performs no type-checking. It is best used for scripts, CLI tools, and developer workflows where the goal is convenience rather than correctness guarantees. Production applications that rely on TypeScript's type-checking for safety should keep a separate tsc or build step in their pipeline.