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.
// 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 !
// 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.
// 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.
// 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.
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é
// 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).
# 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 Runner | Expérimental | ✅ Stable | ✅ Enrichi |
| require(ESM) | ❌ | ❌ | ✅ Stable |
| WebSocket client | ❌ | ❌ | ✅ Natif |
| Permission Model | ❌ | Expérimental | ✅ |
| Glob natif (fs) | ❌ | ❌ | ✅ |
| TypeScript direct | ❌ | ❌ | Expé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.