Node.js : construire et livrer une API
Quitter

Leçon 1 sur 14 · Fondamentaux de Node.js

Node.js : moteur V8, boucle d'événements, versions et REPL

45 min Node.js 22 (Debian, git, npm, python3)

Objectif

Comprendre ce qu'est réellement Node.js et pourquoi il convient si bien aux API (boutique en ligne, réservations, livraison), installer une version LTS avec nvm, et prendre l'habitude de tester des idées dans le REPL et avec de petits scripts.

L'essentiel

  • Node.js est un environnement d'exécution JavaScript hors du navigateur : le moteur V8 (celui de Chrome) + la bibliothèque libuv (entrées/sorties asynchrones) + une bibliothèque standard (node:fs, node:http, node:crypto…).
  • Un programme Node tourne dans un seul processus et un seul fil JavaScript. Il ne bloque pas pendant une lecture disque ou une requête réseau : il continue et reprend quand la réponse arrive, grâce à la boucle d'événements.
  • Conséquence : un serveur Node encaisse des milliers de connexions simultanées… tant que votre code ne fait pas de calcul long et synchrone qui bloque tout le monde.
  • En production, utilisez une version LTS (numéro pair : 22, 24…). Les versions impaires ne vivent que quelques mois.
  • nvm installe et change de version de Node par utilisateur, sans toucher au système ; un fichier .nvmrc fixe la version d'un projet.
  • Le REPL (node sans argument), node -e et node -p servent à essayer une ligne de code en quelques secondes.

Les bases du langage (variables, fonctions, tableaux, promesses) sont traitées dans le cours « Développement web : HTML, CSS et JavaScript ». Ici, nous parlons de ce que Node ajoute autour.

Ce que contient Node.js

            votre code JavaScript (serveur.js)
                         │
   ┌─────────────────────┴──────────────────────┐
   │  bibliothèque standard : node:fs, node:http,│
   │  node:path, node:crypto, node:stream…       │
   ├──────────────────────┬──────────────────────┤
   │  V8 : compile et     │  libuv : boucle      │
   │  exécute le JS       │  d'événements, E/S   │
   │  (JIT, ramasse-      │  asynchrones, pool   │
   │  miettes)            │  de fils pour le     │
   │                      │  disque, DNS, crypto │
   └──────────────────────┴──────────────────────┘
                 système d'exploitation
  • V8 transforme votre JavaScript en code machine à la volée (compilation « juste à temps ») et gère la mémoire (ramasse-miettes). C'est pour cela que Node est rapide.
  • libuv fournit la boucle d'événements et dialogue avec le système (sockets, fichiers, minuteurs). Certaines opérations lentes (lecture de fichiers, hachage de mot de passe, résolution DNS) partent dans un petit pool de fils caché, puis le résultat revient dans la boucle.
  • La bibliothèque standard est riche : on peut écrire un serveur HTTP complet, lire des fichiers, chiffrer, compresser… sans rien installer. Les modules intégrés s'importent avec le préfixe node: (node:http), recommandé pour éviter toute confusion avec un paquet npm du même nom.

Ce qui change par rapport au navigateur

Navigateur Node.js
window, document, le DOM absents : pas de page à afficher
localStorage fichiers (node:fs), bases de données
sandbox : pas d'accès disque accès complet au disque, au réseau, aux processus
version de JS imposée par le navigateur du visiteur vous choisissez la version de Node, donc les fonctionnalités disponibles
globalThis = window globalThis = objet global de Node ; process donne l'environnement

Ce qui existe des deux côtés : console, setTimeout, fetch, URL, JSON, Promise, structuredClone, crypto.randomUUID()…

Le dernier point du tableau est un vrai avantage : sur le serveur, pas besoin d'attendre que les clients mettent à jour leur navigateur. Si votre projet tourne sous Node 22, toute la syntaxe moderne est disponible.

Le premier programme

mkdir -p ~/essais && cd ~/essais
echo 'console.log("Marhaba depuis Node.js");' > bonjour.js
node bonjour.js            # Marhaba depuis Node.js

L'objet process décrit le programme en cours :

// infos.js
console.log(process.version);        // 'v22.x.y'
console.log(process.versions.v8);    // version du moteur V8 embarqué
console.log(process.platform);       // 'linux', 'win32', 'darwin'
console.log(process.argv);           // [chemin de node, chemin du script, arguments…]
console.log(process.cwd());          // dossier courant
console.log(process.env.HOME);       // variables d'environnement
process.exitCode = 0;                // code de sortie (0 = succès)

process.argv contient d'abord le chemin de node, puis celui du script, puis vos arguments, toujours sous forme de texte :

node total.js 120 35.5
# process.argv = ['/usr/bin/node', '/root/total.js', '120', '35.5']
# process.argv.slice(2).map(Number) = [120, 35.5]

La boucle d'événements

Imaginez le serveur d'une application de livraison à Casablanca. Une requête arrive : « liste des commandes du livreur 12 ». Il faut interroger la base, ce qui prend 20 ms. Un serveur « un fil par requête » immobilise un fil pendant ces 20 ms. Node, lui, lance la requête à la base, passe à la requête suivante, et reprend la première quand la base répond.

console.log('A : requête reçue');
setTimeout(() => console.log('C : la base a répondu'), 20);  // simule une E/S
console.log('B : je traite déjà la requête suivante');
// A, B, puis C

Le principe :

  1. Node exécute tout votre script de haut en bas (le code synchrone).
  2. Les opérations asynchrones (minuteurs, réseau, disque) sont confiées à libuv avec une fonction à rappeler.
  3. Quand la pile d'appels est vide, la boucle prend les événements terminés et appelle leurs fonctions, une par une.

Même setTimeout(fn, 0) attend donc que le script en cours soit terminé :

console.log('1 debut');
setTimeout(() => console.log('3 minuteur'), 0);
console.log('2 fin du script');

Les phases, en simplifiant

À chaque tour, la boucle passe par des phases dans un ordre fixe :

Phase Ce qui s'exécute
timers les setTimeout et setInterval arrivés à échéance
poll les entrées/sorties terminées (réseau, fichiers) ; la boucle peut y attendre
check les setImmediate
close les fermetures (socket.on('close'))

Entre deux fonctions de rappel, Node vide aussi deux files prioritaires : celle de process.nextTick, puis celle des microtâches (les .then des promesses).

setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
Promise.resolve().then(() => console.log('promesse'));
process.nextTick(() => console.log('nextTick'));
console.log('synchrone');
// synchrone, nextTick, promesse, puis timeout et immediate
// (l'ordre de ces deux derniers peut varier dans le script principal)

Vous n'avez pas besoin de retenir chaque phase. Retenez la règle d'or.

La règle d'or : ne bloquez pas la boucle

Il n'y a qu'un seul fil pour votre JavaScript. Un calcul de 2 secondes bloque toutes les requêtes pendant 2 secondes :

import { createServer } from 'node:http';

createServer((req, res) => {
  if (req.url === '/rapport') {
    const fin = Date.now() + 2000;
    while (Date.now() < fin) {}          // MAUVAIS : 2 s de calcul synchrone
  }
  res.end('ok\n');
}).listen(3000);
// pendant qu'un client demande /rapport, les autres attendent, même pour '/'

À éviter dans un serveur :

  • les fonctions synchrones d'entrées/sorties (fs.readFileSync, crypto.pbkdf2Sync…) dans le traitement d'une requête : réservez-les au démarrage ;
  • les grosses boucles sur des millions d'éléments, les JSON.parse de fichiers énormes ;
  • les expressions régulières mal écrites sur des textes fournis par l'utilisateur.

L'option node --trace-sync-io serveur.js affiche un avertissement chaque fois qu'une API synchrone est appelée après le démarrage : pratique pour traquer les blocages avant la production. Pour les vrais calculs lourds (génération de PDF, traitement d'images), on utilise des worker_threads ou un service à part.

Les versions de Node.js

Une nouvelle version majeure sort tous les six mois :

  • en avril : version paire (22, 24, 26…) ; elle devient LTS (Long Term Support) en octobre ;
  • en octobre : version impaire (23, 25…), maintenue environ six mois seulement, pour essayer les nouveautés.

Une version LTS passe par trois états :

État Durée approximative Ce qu'elle reçoit
Current 6 mois toutes les nouveautés
Active LTS 12 mois correctifs, améliorations prudentes
Maintenance LTS 18 mois correctifs de sécurité et bugs critiques

Soit environ 30 mois de vie. Situation à la date de ce cours (octobre 2026) :

Version État Fin de vie
Node 20 fin de vie avril 2026 : à migrer
Node 22 Maintenance LTS avril 2027
Node 24 Active LTS avril 2028
Node 26 Current, LTS fin octobre 2026 avril 2029

Vérifiez toujours le calendrier officiel sur le site de Node.js avant de choisir : les dates exactes y sont publiées et le rythme peut évoluer. Le lab de ce cours utilise Node 22.

En pratique, pour une API en production : la LTS active la plus récente, ou la précédente si une bibliothèque n'est pas encore compatible. Jamais une version impaire. Express 5, par exemple, exige au minimum Node 18.

Installer Node.js

Avec nvm (recommandé en développement)

nvm (Node Version Manager) installe Node dans votre dossier personnel. Plusieurs versions cohabitent, sans sudo.

# installation de nvm (script officiel du projet nvm-sh/nvm sur GitHub ;
# vérifiez la dernière version sur la page du projet)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
source ~/.bashrc              # recharger le shell

nvm install --lts             # dernière LTS
nvm install 22                # une branche précise
nvm ls                        # versions installées
nvm use 22                    # utiliser Node 22 dans ce terminal
nvm alias default 22          # version par défaut des nouveaux terminaux
node --version && npm --version

Dans chaque projet, un fichier .nvmrc fixe la version attendue :

echo 22 > .nvmrc
nvm use                       # lit .nvmrc : toute l'équipe a la même version

Sous Windows, utilisez nvm-windows ou fnm, ou l'installateur officiel de nodejs.org.

Sur un serveur

  • Les paquets de la distribution (apt install nodejs) sont souvent anciens : vérifiez la version avant de vous y fier.
  • Les dépôts NodeSource fournissent des paquets .deb/.rpm par branche LTS, mis à jour par apt upgrade.
  • En conteneur, partez de l'image officielle node:22-slim ou node:22-alpine (cours Docker).

Le REPL et les commandes rapides

Lancé sans argument, node ouvre le REPL (Read-Eval-Print Loop) : il lit une expression, l'évalue, affiche le résultat et recommence.

$ node
Welcome to Node.js v22.x.y.
> 120 * 1.2
144
> _ + 10              // _ = le dernier résultat
154
> const tva = 0.2
undefined
> [100, 250].map(p => p * (1 + tva))
[ 120, 300 ]
> 0.1 + 0.2
0.30000000000000004   // les prix en dirhams se calculent en centimes entiers !
> .help               // commandes du REPL
> .editor             // saisir plusieurs lignes, Ctrl+D pour exécuter
> .exit               // ou Ctrl+D

La touche Tab complète les noms : tapez fs. puis Tab pour voir les fonctions du module fs, chargé d'office dans le REPL.

Sans ouvrir de REPL :

node -e 'console.log(new Date().toISOString())'      # exécuter une ligne
node -p '1500 * 0.2'                                 # exécuter ET afficher le résultat : 300
node -p 'process.versions'                           # versions de V8, libuv, OpenSSL…
node --check serveur.js                              # vérifier la syntaxe sans exécuter
node --watch serveur.js                              # relancer à chaque modification (développement)

Pièges fréquents

  • Bloquer la boucle d'événements avec un calcul long ou une fonction …Sync dans une requête : tout le serveur se fige, pour tous les clients.
  • Utiliser une version impaire ou en fin de vie en production : plus de correctifs de sécurité. Suivez la LTS.
  • Installer Node avec sudo apt sans vérifier la version : vous pouvez récupérer une branche dépassée. Vérifiez avec node --version.
  • Oublier que process.argv contient du texte : '120' + '35' donne '12035'. Convertissez avec Number().

Lab

Dans le lab (Node 22, sans réseau), vous allez :

  1. écrire et lancer un premier script ;
  2. relever la version de Node installée ;
  3. lire des arguments de la ligne de commande pour calculer un total en dirhams ;
  4. vérifier par vous-même l'ordre d'exécution imposé par la boucle d'événements.

Pour créer un fichier sans éditeur, utilisez echo '…' > fichier ou cat > fichier <<'EOF' … EOF.

Quiz

  1. Quels sont les trois grands composants de Node.js ?
  2. Pourquoi un serveur Node peut-il gérer des milliers de connexions avec un seul fil ?
  3. Dans quel ordre s'affichent console.log('a'), setTimeout(() => console.log('b'), 0) et console.log('c') écrits dans cet ordre ?
  4. Vous démarrez une API pour un riad en octobre 2026 : Node 25 ou Node 24 ? Pourquoi ?
  5. À quoi sert le fichier .nvmrc ?
Réponses
  1. Le moteur V8 (exécution du JavaScript), libuv (boucle d'événements et entrées/sorties asynchrones) et la bibliothèque standard (node:fs, node:http…).
  2. Parce que les entrées/sorties ne bloquent pas : Node les confie au système, traite d'autres requêtes, puis reprend chaque traitement quand le résultat arrive (boucle d'événements).
  3. a, c, puis b : le minuteur ne s'exécute qu'une fois le script synchrone terminé.
  4. Node 24 : c'est une version paire LTS, maintenue jusqu'en 2028. Node 25 est impaire, sans support long.
  5. À fixer la version de Node d'un projet : nvm use la lit, et toute l'équipe travaille avec la même version.

Sources de référence : documentation officielle de Node.js (nodejs.org, licence MIT) et d'Express (expressjs.com, CC BY-SA 3.0). Texte original MasterLinux.

Exercices pratiques

À réaliser sur Node.js 22 (Debian, git, npm, python3)

Le terminal en ligne ouvre bientôt. En attendant, réalisez ces tâches sur votre machine Linux, une machine virtuelle ou WSL.

  1. Créez /root/bonjour.js qui affiche exactement la ligne Marhaba depuis Node.js, puis lancez-le avec node /root/bonjour.js
    Afficher un indiceecho 'console.log("Marhaba depuis Node.js");' > /root/bonjour.js
    Voir la solution
    echo 'console.log("Marhaba depuis Node.js");' > /root/bonjour.js

    Vérification : test "$(node /root/bonjour.js 2>/dev/null)" = "Marhaba depuis Node.js"

  2. Enregistrez la version de Node.js installée dans le lab (la sortie exacte de node --version, par exemple v22.x.y) dans le fichier /root/version.txt
    Afficher un indiceRedirigez la sortie de la commande : node --version > /root/version.txt
    Voir la solution
    node --version > /root/version.txt

    Vérification : test -s /root/version.txt && test "$(cat /root/version.txt)" = "$(node --version)"

  3. Créez /root/total.js : il additionne les prix passés en arguments (process.argv) et affiche « Total : X MAD » avec deux décimales. node /root/total.js 120 35.5 4.5 doit afficher Total : 160.00 MAD, et sans argument Total : 0.00 MAD
    Afficher un indiceprocess.argv.slice(2) donne les arguments en texte : convertissez-les avec map(Number), additionnez avec reduce, puis total.toFixed(2)
    Voir la solution
    printf '%s\n' 'const prix = process.argv.slice(2).map(Number);' 'const total = prix.reduce((somme, p) => somme + p, 0);' 'console.log(`Total : ${total.toFixed(2)} MAD`);' > /root/total.js

    Vérification : test "$(node /root/total.js 120 35.5 4.5 2>/dev/null)" = "Total : 160.00 MAD" && test "$(node /root/total.js 2>/dev/null)" = "Total : 0.00 MAD"

  4. Observez la boucle d'événements : créez /root/ordre.js qui affiche « 1 debut », programme avec setTimeout(…, 0) l'affichage de « 3 minuteur », puis affiche « 2 fin du script ». L'appel à setTimeout doit être écrit AVANT la ligne qui affiche « 2 fin du script », et la sortie doit être dans l'ordre 1, 2, 3
    Afficher un indiceconsole.log("1 debut"); setTimeout(() => console.log("3 minuteur"), 0); console.log("2 fin du script");
    Voir la solution
    printf '%s\n' 'console.log("1 debut");' 'setTimeout(() => console.log("3 minuteur"), 0);' 'console.log("2 fin du script");' > /root/ordre.js

    Vérification : f=/root/ordre.js; test "$(node $f 2>/dev/null | tr '\n' '|')" = "1 debut|2 fin du script|3 minuteur|" && awk '/setTimeout/{if(!s)s=NR} /2 fin du script/{f=NR} END{exit !(s && f && s<f)}' $f

Références officielles consultées : Introduction to Node.js — https://nodejs.org/en/learn/getting-started/introduction-to-nodejs ; Installation d'Express (prérequis Node.js) — https://expressjs.com/fr/5x/starter/installing/ ; Meilleures pratiques de production : performance et fiabilité — https://expressjs.com/fr/advanced/best-practice-performance. Texte original MasterLinux.