Node.js 22 (LTS depuis octobre 2024) est la version actuelle à adopter pour les nouveaux projets. Elle résout l'un des plus grands points de friction de l'écosystème : l'interopérabilité CJS/ESM avec require() des modules ESM. Elle intègre aussi un client WebSocket natif et le compilateur Maglev de V8 pour de meilleures performances.

Node.js 22 est la version LTS courante recommandée pour les nouveaux projets. Support jusqu'en avril 2027.

1. require() des modules ESM — La fin de la guerre CJS/ESM

C'est la feature la plus impactante de Node 22. Il est désormais possible de require() un module ESM depuis du code CommonJS, sans await import() dynamique.

JavaScript — Avant Node 22
// package.json — "type": "commonjs" (le défaut)
// Problème : un package ESM-only comme chalk 5 → erreur !

// ❌ Ne fonctionnait pas
const chalk = require('chalk'); // Error: require() of ES Module

// ✅ Contournement verbose — import dynamique async
const { default: chalk } = await import('chalk');
// Impossible dans un contexte synchrone !
JavaScript — Node 22
// Node 22 — require() d'un package ESM-only fonctionne !
const chalk = require('chalk');       // ✅
const { glob } = require('glob');     // ✅
const ora = require('ora');           // ✅

// Restriction : le module ESM ne doit pas avoir de await top-level
// (les TLA sont intrinsèquement asynchrones)

console.log(chalk.green('Enfin !'));

// Activer en Node 22.x si pas encore stable
// node --experimental-require-module app.js

2. WebSocket Client — Natif dans Node.js

Node 22 intègre un client WebSocket natif conforme à la spécification WHATWG — identique à l'API navigateur. Plus besoin du package ws pour les cas simples.

JavaScript
// Avant Node 22 — dépendance externe
import WebSocket from 'ws';

// Node 22 — API native identique au navigateur
const ws = new WebSocket('wss://echo.websocket.org');

ws.addEventListener('open', () => {
  console.log('Connexion établie');
  ws.send(JSON.stringify({ type: 'hello', data: 'Node 22!' }));
});

ws.addEventListener('message', ({ data }) => {
  const msg = JSON.parse(data);
  console.log('Reçu :', msg);
});

ws.addEventListener('error', err => {
  console.error('Erreur WebSocket :', err);
});

ws.addEventListener('close', () => {
  console.log('Connexion fermée');
});

// Envoyer des données binaires
ws.send(new Uint8Array([1, 2, 3, 4]));

3. V8 Maglev — Performances JIT améliorées

Node 22 embarque V8 12.4 avec le compilateur Maglev activé par défaut. Maglev est un compilateur JIT de niveau intermédiaire, entre Sparkplug (rapide, peu optimisé) et Turbofan (lent à compiler, très optimisé). Résultat : meilleures performances sur les workloads typiques des serveurs.

JavaScript — Bénéfices Maglev
// Maglev améliore les patterns courants en serveur Node

// Traitement JSON intensif
const data = JSON.parse(largeJsonString);

// Manipulation d'objets fréquente
function processRequest(req) {
  return {
    ...req,
    timestamp: Date.now(),
    id: crypto.randomUUID()
  };
}

// Benchmarks observés : +5 à +20% sur les applications Express/Fastify
// Gains variables selon le profil du code

4. Glob et GlobSync natifs

Node 22 intègre glob et globSync directement dans node:fs, plus besoin du package npm glob.

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

// Version asynchrone
const tsFiles = await glob('**/*.ts', {
  cwd: './src',
  exclude: file => file.includes('node_modules')
});

// Version synchrone
const testFiles = globSync('**/*.test.js', { cwd: './tests' });

// Parcourir les résultats
for (const file of testFiles) {
  console.log('Test :', file);
}

5. AbortSignal.any() et timeout amélioré

JavaScript
// AbortSignal.any() — combine plusieurs signaux
const controller = new AbortController();
const timeout = AbortSignal.timeout(5000);

// Annuler si l'un ou l'autre se déclenche
const combined = AbortSignal.any([controller.signal, timeout]);

try {
  const res = await fetch('https://api.example.com/data', {
    signal: combined
  });
} catch (err) {
  if (err.name === 'TimeoutError') {
    console.log('Timeout dépassé');
  } else if (err.name === 'AbortError') {
    console.log('Annulé manuellement');
  }
}

// Annuler manuellement
controller.abort();

6. Strip TypeScript (Node 22.6+ expérimental)

Node 22.6 introduit le support expérimental d'exécution directe de fichiers TypeScript (en supprimant les annotations de types, sans transpilation complète).

Shell — TypeScript natif en Node
# Node 22.6+ — exécuter directement du TypeScript
node --experimental-strip-types monFichier.ts

# Exemple monFichier.ts
interface User {
  name: string;
  age: number;
}

const user: User = { name: 'Kriss', age: 30 };
console.log(user.name);

# Note : les types sont supprimés (strip), pas compilés.
# Pas de vérification de types — pour ça, utilisez tsc.
# Stable en Node 23 / Node 24+

7. Récapitulatif Node.js 18 → 22

Feature Node 18 Node 20 Node 22
Fetch API✅ Natif
Test RunnerExpérimental✅ Stable✅ Enrichi
require(ESM)✅ Stable
WebSocket client✅ Natif
Permission ModelExpérimental
Glob natif (fs)
TypeScript directExpérimental
V8 Maglev✅ Activé
🔗

require(ESM)

Interopérabilité CJS/ESM enfin résolue. Plus de refus des packages modernes.

🔌

WebSocket natif

API identique au navigateur. Code isomorphique sans librairie externe.

V8 Maglev

Compilateur JIT intermédiaire. +5 à +20% de performance sur code serveur.

📂

Glob natif

node:fs intègre glob/globSync — une dépendance npm de moins.

🟦

TypeScript direct

node --experimental-strip-types — exécution de .ts sans build.

🛑

AbortSignal.any()

Combinaison de signaux d'annulation pour fetch, streams, timers.