Node.js 22 Promoted to LTS - require(ESM) and New Features Summary

2025.12.02

Node.js 22 LTS Overview

Node.js 22 was promoted to LTS (Long Term Support) in October 2024. Codenamed “Jod”, it will receive active support until April 2027.

VersionReleaseLTS StartEnd
Node.js 182022/042022/102025/04
Node.js 202023/042023/102026/04
Node.js 222024/042024/102027/04
Node.js 242025/042025/102028/04

Recommended: Use LTS versions in production

require() ESM Support

The biggest highlight of Node.js 22 is the ability to load ES modules using require() from CommonJS.

// ESM package (package.json: "type": "module")
// lib.mjs
export const greeting = 'Hello';
export function sayHello(name) {
  return `${greeting}, ${name}!`;
}
export default { greeting, sayHello };

// Now loadable from CommonJS
// app.cjs
const { greeting, sayHello } = require('./lib.mjs');
console.log(sayHello('World')); // Hello, World!

// Default export also available
const lib = require('./lib.mjs');
console.log(lib.default.greeting); // Hello

Promotion from Experimental Flag

# Before Node.js 22 (experimental)
node --experimental-require-module app.js

# Node.js 22.12.0+ (enabled by default)
node app.js

# To disable
node --no-experimental-require-module app.js

Caveats

// ESM with top-level await cannot be required
// async-lib.mjs
const data = await fetch('https://api.example.com/data');
export const config = await data.json();

// This will error
try {
  require('./async-lib.mjs'); // ERR_REQUIRE_ASYNC_MODULE
} catch (e) {
  console.error('Cannot require modules with top-level await');
}

WebSocket Client (Built-in)

// Node.js 22+: WebSocket available without external libraries
const ws = new WebSocket('wss://echo.websocket.org');

ws.addEventListener('open', () => {
  console.log('Connected');
  ws.send('Hello, WebSocket!');
});

ws.addEventListener('message', (event) => {
  console.log('Received:', event.data);
});

ws.addEventListener('close', () => {
  console.log('Disconnected');
});

ws.addEventListener('error', (error) => {
  console.error('Error:', error);
});

HTTP Server Integration

import { createServer } from 'http';
import { WebSocketServer } from 'ws';

const server = createServer();
const wss = new WebSocketServer({ server });

wss.on('connection', (ws, request) => {
  console.log('New connection:', request.url);

  ws.on('message', (message) => {
    // Broadcast
    wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(message.toString());
      }
    });
  });
});

server.listen(8080, () => {
  console.log('WebSocket server running on ws://localhost:8080');
});

V8 Engine 12.4

Node.js 22 ships with V8 12.4, enabling the latest JavaScript features.

Array.fromAsync

// Create array from async iterable
async function* asyncGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

const arr = await Array.fromAsync(asyncGenerator());
console.log(arr); // [1, 2, 3]

// Also handles Promise arrays
const promises = [
  fetch('/api/user/1'),
  fetch('/api/user/2'),
  fetch('/api/user/3'),
];

const responses = await Array.fromAsync(promises, (p) => p.then((r) => r.json()));

Set Method Additions

const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5, 6]);

// Union
console.log(a.union(b)); // Set {1, 2, 3, 4, 5, 6}

// Intersection
console.log(a.intersection(b)); // Set {3, 4}

// Difference
console.log(a.difference(b)); // Set {1, 2}

// Symmetric difference
console.log(a.symmetricDifference(b)); // Set {1, 2, 5, 6}

// Subset check
console.log(new Set([1, 2]).isSubsetOf(a)); // true
console.log(a.isSupersetOf(new Set([1, 2]))); // true

// Disjoint check
console.log(a.isDisjointFrom(new Set([7, 8]))); // true

Iterator Helpers

// Transform operations on iterators
function* numbers() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
}

// map, filter, take available
const result = numbers()
  .filter((n) => n % 2 === 0)
  .map((n) => n * 2)
  .take(2)
  .toArray();

console.log(result); // [4, 8]

// drop: skip from beginning
const skipped = numbers().drop(2).toArray();
console.log(skipped); // [3, 4, 5]

// flatMap
function* nested() {
  yield [1, 2];
  yield [3, 4];
}
const flat = nested()
  .flatMap((arr) => arr)
  .toArray();
console.log(flat); // [1, 2, 3, 4]

// find
const found = numbers().find((n) => n > 3);
console.log(found); // 4

// some / every
console.log(numbers().some((n) => n > 3)); // true
console.log(numbers().every((n) => n > 0)); // true

Built-in SQLite (Experimental)

import { DatabaseSync } from 'node:sqlite';

// In-memory database
const db = new DatabaseSync(':memory:');

// Create table
db.exec(`
  CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT UNIQUE
  )
`);

// Insert data
const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
insert.run('Alice', 'alice@example.com');
insert.run('Bob', 'bob@example.com');

// Execute query
const select = db.prepare('SELECT * FROM users WHERE name = ?');
const user = select.get('Alice');
console.log(user); // { id: 1, name: 'Alice', email: 'alice@example.com' }

// Get all
const all = db.prepare('SELECT * FROM users').all();
console.log(all);

// Transaction
const transfer = db.transaction((from, to, amount) => {
  db.prepare('UPDATE accounts SET balance = balance - ? WHERE id = ?').run(amount, from);
  db.prepare('UPDATE accounts SET balance = balance + ? WHERE id = ?').run(amount, to);
});

transfer(1, 2, 100);

Performance Improvements

MetricNode.js 20Node.js 22Improvement
Startup time (Hello World)120ms85ms29% faster
HTTP server (req/sec)45,00052,00015% faster
File I/O (1000 file reads)280ms230ms18% faster
Stream processing320ms250ms22% faster

Watch Mode Stabilization

# Watch files and auto-restart
node --watch server.js

# Watch specific paths only
node --watch-path=./src --watch-path=./config server.js

# Preserve output during watch
node --watch --watch-preserve-output server.js
// Programmatic watch status check
import { watch } from 'node:fs/promises';

const ac = new AbortController();
const { signal } = ac;

(async () => {
  const watcher = watch('./src', { recursive: true, signal });
  for await (const event of watcher) {
    console.log(`${event.eventType}: ${event.filename}`);
  }
})();

// Stop watching
// ac.abort();

Native Glob Pattern Support

import { glob, globSync } from 'node:fs';

// Async API
const files = await glob('**/*.js', {
  cwd: './src',
  ignore: ['node_modules/**'],
});
console.log(files);

// Sync API
const syncFiles = globSync('**/*.{ts,tsx}');
console.log(syncFiles);

// Also available via fs module
import { promises as fs } from 'node:fs';
const matches = await fs.glob('src/**/*.test.ts');

Environment Variable File Loading

# Auto-load .env file
node --env-file=.env app.js

# Multiple files supported
node --env-file=.env --env-file=.env.local app.js
# .env
DATABASE_URL=postgresql://localhost:5432/mydb
API_KEY=secret-key
NODE_ENV=development
// Access environment variables
console.log(process.env.DATABASE_URL);
console.log(process.env.API_KEY);

Test Runner Improvements

// test/user.test.js
import { describe, it, before, after, mock } from 'node:test';
import assert from 'node:assert/strict';

describe('User Service', () => {
  let mockDb;

  before(() => {
    mockDb = mock.fn(() => ({ id: 1, name: 'Test User' }));
  });

  after(() => {
    mock.reset();
  });

  it('should create a user', async () => {
    const user = await createUser({ name: 'Test User' });
    assert.strictEqual(user.name, 'Test User');
  });

  it('should validate email', () => {
    assert.throws(() => createUser({ email: 'invalid' }), /Invalid email/);
  });

  // Snapshot testing
  it('should match snapshot', async (t) => {
    const user = await getUser(1);
    t.assert.snapshot(user);
  });
});
# Run tests
node --test

# With coverage
node --test --experimental-test-coverage

# Specific file only
node --test test/user.test.js

Migration Guide

# Check version
node --version

# Using nvm
nvm install 22
nvm use 22
nvm alias default 22

# Check dependency compatibility
npx npm-check-updates --target minor

# package.json engine specification
# "engines": { "node": ">=22" }
← Back to list