Chapitre 117 minIntermédiaire
C'est quoi Node.js ? Définition, installation et premier script
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- expliquer ce qu'est Node.js et ce qui le distingue de JavaScript et d'Express ;
- décrire le rôle de la boucle d'événements et du code non bloquant ;
- choisir et installer une version LTS de Node.js ;
- exécuter un script et lire un fichier avec
node:fs/promises.
5 exercices corrigés1 schéma
Node.js est un environnement d'exécution JavaScript : un programme qui fait tourner du JavaScript hors du navigateur, directement sur un ordinateur ou un serveur. Il sert surtout à écrire des serveurs web et des API, mais aussi des outils en ligne de commande et des scripts. Ce chapitre explique ce qu'est Node.js, pourquoi il gère très bien beaucoup de requêtes à la fois, quelle version installer, puis te fait écrire tes premiers scripts.
Node.js, JavaScript et Express : qui fait quoi
Trois mots reviennent sans cesse, et les confondre fait perdre des points à l'oral.
| Terme | Ce que c'est | Analogie |
|---|---|---|
| JavaScript | Un langage de programmation, normalisé sous le nom ECMAScript | La langue dans laquelle tu écris |
| Node.js | Un environnement d'exécution (runtime) : le moteur V8 de Chrome, plus des modules pour accéder aux fichiers, au réseau, aux processus | L'interprète qui lit ta langue et agit sur la machine |
| Express | Un framework web écrit pour Node.js : routes, middlewares, réponses HTTP | Un manuel de procédures qui t'évite de tout réinventer |
Node.js n'est donc ni un langage ni un framework. Tu écris du JavaScript ; Node.js l'exécute ; Express t'aide à structurer un serveur web. Un navigateur est aussi un environnement d'exécution JavaScript, mais il offre d'autres objets : document et window existent dans le navigateur et pas dans Node.js ; à l'inverse, Node.js donne accès au disque (node:fs), aux processus (process) et au réseau bas niveau (node:net), interdits à une page web.
Node.js a été créé en 2009 par Ryan Dahl. Il est libre (licence MIT) et piloté par l'OpenJS Foundation. Il est installé avec npm, le gestionnaire de paquets qui donne accès à des millions de bibliothèques (chapitre 2).
À quoi sert Node.js
- Des API et des back-ends web : c'est l'usage de ce cours. Le front (React, une application mobile) envoie des requêtes HTTP ; l'API Node.js répond en JSON.
- Du temps réel : messagerie, notifications, tableaux de bord mis à jour en direct (WebSocket).
- Des outils de développement : Vite, ESLint, Prettier, TypeScript et le serveur de développement de React tournent tous sous Node.js. Même si tu ne codes pas de back-end en JavaScript, tu utilises Node.js dès que tu fais du front moderne.
- Des scripts : traiter un fichier CSV, appeler une API la nuit, générer un rapport.
Node.js est moins adapté aux calculs lourds qui monopolisent le processeur (traitement d'image, calcul scientifique) : la section sur la boucle d'événements explique pourquoi.
| Critère | PHP (avec PHP-FPM) | Node.js |
|---|---|---|
| Qui démarre le programme | Le serveur web lance un script PHP pour chaque requête | Ton programme démarre une fois et reste en mémoire |
| Requêtes simultanées | Plusieurs processus PHP, un par requête en cours | Un seul processus qui alterne entre les requêtes pendant les attentes |
| Serveur HTTP | Apache ou Nginx devant PHP | Intégré : ton code écoute lui-même un port |
| Langage du front et du back | Deux langages | Le même JavaScript partout |
Aucune des deux approches n'est « meilleure » : PHP est simple à héberger et omniprésent, Node.js brille quand l'application passe son temps à attendre la base de données ou d'autres services, ce qui est le cas de la plupart des API.
Le moteur V8 et la boucle d'événements
Node.js exécute ton JavaScript avec V8, le moteur de Google Chrome, qui compile le code en langage machine à la volée. Ton code tourne sur un seul fil d'exécution (thread) : une seule instruction JavaScript s'exécute à un instant donné.
Comment un seul fil peut-il servir des centaines de requêtes ? Parce qu'une API passe l'essentiel de son temps à attendre : la réponse de MySQL, la lecture d'un fichier, la réponse d'un autre service. Node.js ne reste pas les bras croisés pendant ces attentes. Il confie l'opération au système d'exploitation (ou à un petit groupe de fils internes, géré par la bibliothèque libuv), passe à la requête suivante, et reprend le traitement quand le résultat arrive. C'est le modèle asynchrone non bloquant, orchestré par la boucle d'événements (event loop).
Voici ce que ça change concrètement. Ce script affiche quatre lignes ; devine l'ordre avant de lire le résultat.
console.log('1. début du script');
setTimeout(() => console.log('4. minuteur à 0 ms'), 0);
Promise.resolve().then(() => console.log('3. promesse résolue'));
console.log('2. fin du script');
1. début du script
2. fin du script
3. promesse résolue
4. minuteur à 0 ms
Le code synchrone s'exécute d'abord, jusqu'au bout. Ensuite seulement, la boucle traite ce qui attend : d'abord les suites de promesses (les microtâches), puis les minuteurs. Un setTimeout(…, 0) ne veut pas dire « tout de suite », mais « dès que possible, quand la pile est vide ».
Ne bloque jamais la boucle
Puisqu'il n'y a qu'un fil pour ton code, une instruction qui l'occupe longtemps bloque toutes les requêtes en même temps. Avec PHP, un script lent ne pénalise que son propre visiteur ; avec Node.js, il fige tout le serveur.
// À NE PAS FAIRE dans une route : 3 secondes de calcul pendant lesquelles
// aucune autre requête n'est servie, même /sante
const fin = Date.now() + 3000;
while (Date.now() < fin) { /* le processeur tourne à vide */ }
Les causes classiques, et leur remède :
| Ce qui bloque | Le remède |
|---|---|
Une fonction synchrone d'E/S dans une route : readFileSync, execSync | La version asynchrone : await readFile(…) de node:fs/promises |
Un hachage de mot de passe synchrone (bcrypt.hashSync) | La version asynchrone (await argon2.hash(…), await bcrypt.hash(…)) |
| Une boucle sur des centaines de milliers de lignes | Faire le travail dans la base (WHERE, GROUP BY), paginer |
| Un vrai calcul lourd (image, PDF, chiffrement de gros fichiers) | Un worker (node:worker_threads) ou un service séparé |
Les fonctions …Sync ne sont pas interdites partout : dans un script lancé à la main, ou au démarrage du serveur avant qu'il n'accepte des requêtes, elles sont acceptables. Dans le traitement d'une requête, jamais.
Quelle version de Node.js choisir ?
Une nouvelle version majeure de Node.js sort tous les six mois. Les versions paires deviennent LTS (Long Term Support) en octobre : elles reçoivent ensuite des correctifs pendant deux ans et demi environ. Les versions impaires restent « Current » quelques mois puis s'arrêtent. Pour un projet, prends toujours une LTS.
| Version | Nom de code | LTS depuis | Phase au 2 novembre 2026 | Fin de maintenance |
|---|---|---|---|---|
| Node.js 22 | Jod | octobre 2024 | Maintenance (correctifs de sécurité) | 30 avril 2027 |
| Node.js 24 | Krypton | octobre 2025 | Maintenance depuis le 20 octobre 2026 | 30 avril 2028 |
| Node.js 26 | Lithium | 28 octobre 2026 | LTS active | 30 avril 2029 |
Le calendrier officiel du projet change à partir de Node.js 27, prévu en avril 2027 : une seule version majeure par an, qui deviendra LTS à l'automne suivant. Vérifie toujours la page des versions sur nodejs.org avant de démarrer un projet.
Installer Node.js
Trois façons de l'installer :
- L'installeur officiel (nodejs.org, bouton LTS) : le plus simple sous Windows et macOS. Il installe
nodeetnpm. - Un gestionnaire de versions :
nvm(Linux, macOS),nvm-windowsoufnm(tous systèmes). Il permet d'avoir plusieurs versions et d'en changer par projet. C'est le choix des développeurs qui jonglent entre plusieurs projets. - Un conteneur Docker (
node:24-slim) : la même version pour toute l'équipe, sans rien installer sur le poste (chapitre 14).
# Avec nvm : installer la LTS 24 et l'utiliser
nvm install 24
nvm use 24
# Vérifier
node --version # v24.15.0 (le dernier chiffre change avec les correctifs)
npm --version # 11.12.1
Sous Linux, évite le paquet nodejs des dépôts de ta distribution s'il est ancien : prends nvm ou le dépôt officiel de NodeSource.
Premier script et REPL
Crée un fichier bonjour.js :
const prenom = process.argv[2] ?? 'inconnu';
console.log(`Bonjour ${prenom}, Node.js ${process.version} tourne sur ${process.platform}.`);
node bonjour.js Lucas
Bonjour Lucas, Node.js v24.15.0 tourne sur darwin.
process est un objet global de Node.js : process.argv contient les arguments de la ligne de commande (argv[0] est le chemin de node, argv[1] celui du script, puis tes arguments), process.env les variables d'environnement, process.exitCode le code de retour du programme. process.platform vaut win32 sous Windows, linux sous Linux, darwin sous macOS.
Lancé sans fichier, node ouvre le REPL (Read-Eval-Print Loop), une console où tu tapes du JavaScript et lis le résultat immédiatement. Pratique pour tester une méthode de tableau ou une expression régulière. .exit (ou deux fois Ctrl+C) pour en sortir.
Lire un fichier avec node:fs/promises
Les modules intégrés à Node.js s'importent avec le préfixe node:. Ce script lit le fichier tarifs.json placé à côté de lui et affiche les forfaits de frais d'AppliFrais :
[
{ "code": "ETP", "libelle": "Forfait étape", "montant": 110 },
{ "code": "KM", "libelle": "Frais kilométriques", "montant": 0.62 },
{ "code": "NUI", "libelle": "Nuitée hôtel", "montant": 80 },
{ "code": "REP", "libelle": "Repas restaurant", "montant": 25 }
]
// tarifs.js
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
const chemin = join(import.meta.dirname, 'tarifs.json');
try {
const texte = await readFile(chemin, 'utf8');
const tarifs = JSON.parse(texte);
for (const { code, libelle, montant } of tarifs) {
console.log(`${code.padEnd(4)}${libelle.padEnd(22)}${montant.toFixed(2).padStart(7)} €`);
}
} catch (erreur) {
if (erreur.code === 'ENOENT') {
console.error(`Fichier introuvable : ${chemin}`);
process.exitCode = 1;
} else {
throw erreur;
}
}
ETP Forfait étape 110.00 €
KM Frais kilométriques 0.62 €
NUI Nuitée hôtel 80.00 €
REP Repas restaurant 25.00 €
Quatre détails à retenir :
- ce fichier utilise
import: le projet doit être en modules ES, avec"type": "module"dans lepackage.json(chapitre 2). Avec"type": "commonjs", la valeur quenpm initécrit par défaut, Node.js s'arrête surSyntaxError: Cannot use import statement outside a module; sans champtype, il relit le fichier comme module ES et affiche un avertissement ; awaitest permis directement au niveau du module (top-level await), sans fonctionasyncautour ;import.meta.dirnameest le dossier du fichier en cours. Sans lui, un chemin relatif comme'tarifs.json'est résolu depuis le dossier où tu as lancé la commande, pas depuis celui du script : une source d'erreurs fréquente ;- une erreur système porte un code lisible :
ENOENT(fichier absent),EACCES(droits insuffisants),EADDRINUSE(port déjà utilisé),ECONNREFUSED(service injoignable, souvent MySQL arrêté). Testeerreur.codeplutôt que le texte du message.
Les modules intégrés à connaître
| Module | Sert à | Exemple |
|---|---|---|
node:fs/promises | Lire et écrire des fichiers et des dossiers | await writeFile('export.csv', texte) |
node:path | Construire des chemins valables sous Windows comme sous Linux | join(import.meta.dirname, 'stockage') |
node:crypto | Identifiants aléatoires, hachage, chiffrement | randomUUID() |
node:http | Créer un serveur HTTP (Express s'appuie dessus) | createServer(…) |
node:test et node:assert | Écrire et lancer des tests (chapitre 12) | assert.equal(total, 898.9) |
node:os | Informations sur la machine | os.cpus().length |
Certaines fonctions sont globales et n'ont pas besoin d'import : console, process, setTimeout, structuredClone, et surtout fetch, le même que dans le navigateur, pour appeler une autre API depuis ton serveur.
Exercices
Exercice 1 sur 5 L'ordre d'affichage Réussi À revoir
Dans quel ordre ce script affiche-t-il les lettres ? Justifie.
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
queueMicrotask(() => console.log('D'));
console.log('E');
Voir la réponseMasquer la réponse de l'exercice 1
A, E, C, D, B.
Le code synchrone s'exécute d'abord jusqu'au bout : A puis E. Quand la pile d'appels est vide, Node.js traite toutes les microtâches, dans l'ordre où elles ont été mises en file : la suite de la promesse (C), puis la fonction passée à queueMicrotask (D). Les minuteurs ne passent qu'ensuite : B, même avec un délai de 0 ms.
Tu avais trouvé ?
Exercice 2 sur 5 Langage, environnement ou framework ? Réussi À revoir
Classe chaque élément : JavaScript, Node.js, Express, V8, npm, React. Puis corrige la phrase d'un camarade : « Mon back-end est codé en Node.js, qui est un framework JavaScript. »
Voir la réponseMasquer la réponse de l'exercice 2
- JavaScript : un langage.
- Node.js : un environnement d'exécution.
- V8 : le moteur JavaScript de Chrome, utilisé par Node.js.
- Express : un framework web pour Node.js.
- npm : le gestionnaire de paquets fourni avec Node.js.
- React : une bibliothèque d'interface, côté front.
Phrase corrigée : « Mon back-end est écrit en JavaScript, exécuté par Node.js, avec le framework Express. »
Tu avais trouvé ?
Exercice 3 sur 5 Bloquant ou pas ? Réussi À revoir
Pour chaque ligne placée dans une route d'API, dis si elle bloque la boucle d'événements, et propose une correction si c'est le cas.
const texte = readFileSync('modele.html', 'utf8');const [lignes] = await pool.execute('SELECT * FROM fiche_frais WHERE id = ?', [id]);const hache = bcrypt.hashSync(motDePasse, 12);const reponse = await fetch('https://api.example/taux');- Une boucle
forqui additionne les montants de 200 000 lignes chargées en mémoire.
Voir la réponseMasquer la réponse de l'exercice 3
- Bloque : lecture synchrone. Utilise
await readFile(…)denode:fs/promises, ou lis le fichier une seule fois au démarrage du serveur. - Ne bloque pas : la requête part vers MySQL, Node.js sert d'autres requêtes en attendant la réponse.
- Bloque, et longtemps : un hachage de mot de passe est volontairement lent. Utilise la version asynchrone,
await bcrypt.hash(motDePasse, 12), ou mieuxawait argon2.hash(motDePasse)(chapitre 9). - Ne bloque pas : appel réseau asynchrone.
- Bloque pendant toute la boucle, et charger 200 000 lignes en mémoire est déjà une erreur. Fais faire le calcul à la base :
SELECT SUM(montant) FROM ….
Tu avais trouvé ?
Exercice 4 sur 5 Choisir une version Réussi À revoir
Le 2 novembre 2026, ton équipe démarre la réalisation qu'elle présentera à l'E6 en mai 2027. Un camarade propose Node.js 25 « parce que c'est la plus récente après la 26 ». Que lui réponds-tu ? Quelle version choisis-tu, et où l'écris-tu ?
Voir la réponseMasquer la réponse de l'exercice 4
Node.js 25 est une version impaire : elle n'est jamais LTS et n'est plus maintenue. Il faut une LTS : Node.js 26 (LTS active depuis le 28 octobre 2026, maintenue jusqu'en avril 2029) ou Node.js 24 (maintenue jusqu'en avril 2028), toutes deux couvertes jusqu'à l'épreuve. L'important est de choisir une version et de l'écrire partout : champ engines du package.json, image du Dockerfile (node:24-slim ou node:26-slim), version du pipeline de CI, et fiche descriptive de la réalisation.
Tu avais trouvé ?
Exercice 5 sur 5 Un script utile Réussi À revoir
Écris un script total.js qui lit tarifs.json (le fichier de ce chapitre) et affiche le montant des frais forfaitisés d'une fiche de 3 étapes, 420 km, 2 nuitées et 5 repas. Le résultat attendu est 875.40 €.
Voir la réponseMasquer la réponse de l'exercice 5
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
const tarifs = JSON.parse(await readFile(join(import.meta.dirname, 'tarifs.json'), 'utf8'));
const quantites = { ETP: 3, KM: 420, NUI: 2, REP: 5 };
let centimes = 0;
for (const { code, montant } of tarifs) {
centimes += quantites[code] * Math.round(montant * 100);
}
console.log(`${(centimes / 100).toFixed(2)} €`);
3 × 110 + 420 × 0,62 + 2 × 80 + 5 × 25 = 330 + 260,40 + 160 + 125 = 875,40 €. Le calcul se fait en centimes : en virgule flottante, certains produits tombent faux, par exemple 57 * 0.62 donne 35.339999999999996 et 3 * 0.62 donne 1.8599999999999999. Pour de l'argent, compte en entiers, puis divise par 100 à l'affichage.
Tu avais trouvé ?