2.1 · Modules natifs : fs, path, os

Niveau 2 · Cœur de Node.js

2.1Modules natifs : fs, path, os

Objectif : Exploiter les modules natifs de Node (fs en asynchrone, path portable, os) : les capacités système sans dépendance externe.
Temps estimé : 11 min · lecture autonome · Si vous n'avez que 5 min : lisez « L'essentiel en 30s » + le schéma.

L'essentiel en 30 secondes

  • Intégrés, sans installation : fs, path, os sont livrés avec Node et s'importent par leur nom — moins de dépendances, plus léger, plus sûr.
  • fs = asynchrone par défaut : fs.promises + await ne bloque pas la boucle d'événements — le Sync bloque le serveur pour tout le monde.
  • path rend portable, os renseigne : path.join gère le bon séparateur sur chaque système, os expose plateforme et mémoire — jamais de chemins concaténés à la main.

Mini-cas filé — Salim inventorie les rapports à Alger

Salim, développeur junior à Alger, doit outiller son équipe : chaque semaine, des dizaines de rapports texte arrivent dans un dossier partagé, et il faut les recenser (nom, taille, date) dans un fichier d'inventaire. Il écrit son premier script Node sans rien installer : fs lit et écrit les fichiers, path construit des chemins qui marchent aussi bien sur son Windows que sur le serveur Linux de l'équipe, os affiche la plateforme pour le journal. Ce même chantier revient en 2.2 (signaler la progression par événements), 2.3 (gros rapports traités en stream) et 2.4 (servir l'inventaire en HTTP).

Fil conducteur du niveau 2 : ce même chantier de Salim revient dans le cours, le schéma, le script et le défi 48h — et continue en 2.2, 2.3 et 2.4.

1. Des modules natifs, sans rien installer

Node fournit des modules natifs (built-in) : des bibliothèques intégrées, disponibles sans rien installer, qui donnent accès aux capacités du système — fichiers, réseau, système d'exploitation. C'est ce qui fait de Node bien plus qu'un simple exécuteur de JavaScript. On les importe par leur nom seul (require('fs'), avec le préfixe explicite node: possible : require('node:fs')). Le plus utilisé est fs (file system) : lire, écrire, modifier, supprimer fichiers et dossiers. L'intérêt est double : couvrir les besoins courants sans dépendance externe, et comprendre le fondement des bibliothèques (Express repose sur le module http). Salim l'a vérifié : son inventaire ne demande aucun paquet npm.

2. fs : l'asynchrone d'abord, le Sync presque jamais

Point crucial : la plupart des fonctions de fs existent en deux versions — asynchrone (non-bloquante, à privilégier : fs.readFile avec callback, ou mieux fs.promises.readFile avec await) et synchrone (bloquante, suffixée Sync : fs.readFileSync). En serveur, on utilise l'asynchrone pour ne pas bloquer la boucle d'événements : un seul readFileSync lent gèle le serveur pour tous les clients. Le synchrone ne se justifie que dans des cas précis — petits scripts ponctuels, chargement de config au démarrage. Salim écrit donc son inventaire en fs.promises + async/await : moderne, lisible, non-bloquant.

3. path pour la portabilité, os pour le système

Le module path manipule les chemins de façon portable : le séparateur est / sur macOS/Linux mais \ sur Windows, donc on n'assemble jamais un chemin à la main. path.join(__dirname, 'data', 'fichier.txt') utilise le bon séparateur selon le système ; path.resolve() donne un chemin absolu, path.basename(), path.extname() et path.dirname() en extraient les morceaux. Le module os expose le système : os.platform(), os.cpus() (utile pour le clustering, section 5-2), os.totalmem() / os.freemem(), os.homedir(), os.tmpdir(). D'autres natifs existent (crypto, util, url…) : pas besoin de tous les mémoriser, mais savoir qu'ils existent et consulter la doc officielle.

Erreur d'interprétation fréquente : « Pour aller vite, j'utilise les versions Sync partout — c'est plus simple à lire ». Non. Sur un serveur, chaque appel synchrone bloque l'unique thread de Node pendant toute l'opération : pendant que readFileSync lit un fichier, le serveur ne répond à aucun autre utilisateur. Le « plus simple » apparent est une faute de performance grave dès qu'il y a des clients simultanés. La règle : en serveur, toujours l'asynchrone (fs.promises + await) ; le Sync est réservé aux petits scripts ponctuels et au chargement de config au démarrage.
Les trois natifs du quotidien Système sans dépendance fsLire/écrire en asynchrone (promises + await)+pathChemins portables (join, resolve, basename)+osInfos système (plateforme, CPU, mémoire) Natifs = importés par leur nom, zéro installation.
Système sans dépendance
Version texte du schéma (SEO / lecteurs d'écran / traduction) : 1) Salim importe les NATIFS par leur nom (require('fs'), require('path'), require('os')) — rien à installer. 2) Il lit et écrit l'inventaire avec fs.promises + await : ASYNCHRONE, le serveur reste réactif pour tous. 3) Il assemble les chemins avec path.join : PORTABLES entre son Windows et le Linux de l'équipe. 4) Il journalise la plateforme et la mémoire avec os. Leçon : les natifs couvrent les besoins courants sans dépendance — et fondent les bibliothèques comme Express.

🔎 Exemple concret — 🔎 Exemple concret — L'inventaire de Salim en asynchrone

Trois natifs combinés, comme dans le script de Salim :

const fs = require('node:fs/promises');
const path = require('node:path');
const os = require('node:os');

const dossier = path.join(__dirname, 'rapports');
const fichiers = await fs.readdir(dossier);
const inventaire = path.join(dossier, 'inventaire.txt');
await fs.writeFile(inventaire, fichiers.join('\n'));
console.log('Plateforme :', os.platform());

Lecture : aucun paquet installé, chemins portables, écriture non-bloquante.

Leçon : fs.promises + await en serveur, Sync réservé aux scripts.

Vocabulaire de la section

Modules natifs (built-in)
Bibliothèques intégrées à Node, importées par leur nom sans installation : fs, path, os, http, crypto, events, stream…

Exemple simple : Salim importe fs, path et os sans toucher à npm.

Ce que ce n'est pas : un paquet npm tiers à installer et mettre à jour.

Réflexe : besoin courant = vérifier le natif avant d'installer.

fs (file system)
Module de lecture/écriture de fichiers et dossiers, en version asynchrone (à privilégier, fs.promises + await) ou synchrone (Sync, bloquante).

Exemple simple : Salim recense les rapports avec fs.readdir et fs.writeFile.

Ce que ce n'est pas : un module de routage web (c'est Express, niveau 3).

Réflexe : fichiers en serveur = fs.promises + await.

Asynchrone vs synchrone (fs)
L'asynchrone (non-bloquant) laisse Node servir d'autres requêtes pendant l'opération ; le synchrone (Sync) bloque l'unique thread.

Exemple simple : L'inventaire de Salim s'écrit sans geler le serveur.

Ce que ce n'est pas : deux syntaxes interchangeables sans conséquence.

Réflexe : serveur = asynchrone ; Sync = scripts et config au démarrage.

path
Module de manipulation portable des chemins (join, resolve, basename, extname, dirname) qui gère le séparateur de chaque système.

Exemple simple : path.join(__dirname, 'rapports') marche sur le Windows de Salim et le Linux de l'équipe.

Ce que ce n'est pas : une concaténation à la main avec des slashs en dur.

Réflexe : un chemin à construire = path.join.

os
Module d'accès aux informations système : plateforme, processeurs (os.cpus), mémoire, dossiers standard (homedir, tmpdir).

Exemple simple : Salim journalise os.platform() et os.freemem() avec son inventaire.

Ce que ce n'est pas : un module de lecture de fichiers.

Réflexe : infos machine (clustering, diagnostic) = penser os.

En pratique — En pratique — Utiliser les modules natifs

  1. Lisez et écrivez un fichier avec fs.promises et async/await (version asynchrone, non-bloquante) ; comparez à fs.readFileSync (synchrone, bloquante).
  2. Construisez des chemins portables avec path.join(__dirname, 'dossier', 'fichier.txt') plutôt qu'en concaténant à la main.
  3. Extrayez des informations d'un chemin : path.basename, path.extname, path.dirname.
  4. Affichez des infos système avec os : os.platform(), os.cpus().length, os.freemem().
Vous exploitez les modules natifs de Node (fs pour les fichiers en asynchrone, path pour les chemins portables, os pour le système) — les capacités système sans dépendance externe.

Script prêt à dire — justifier le tout-natif à son lead (30 s)

« Pour l'inventaire des rapports, je n'ai rien installé : fs lit et écrit les fichiers en asynchrone avec fs.promises, path construit des chemins qui marchent sur Windows comme sur Linux, et os journalise la plateforme. Zéro dépendance, donc zéro mise à jour à suivre — et ce sont les mêmes briques que celles sur lesquelles Express est bâti. »

À dire en montrant le script qui tourne sur les deux systèmes ; l'argument qui convainc est l'absence de dépendance.

Modèle copier-coller — inventaire de dossier en asynchrone

const fs = require('node:fs/promises');
const path = require('node:path');
const os = require('node:os');

async function inventaire(nomDossier) {
  const dossier = path.join(__dirname, nomDossier);
  const fichiers = await fs.readdir(dossier);
  const lignes = [];
  for (const f of fichiers) {
    const chemin = path.join(dossier, f);
    const stats = await fs.stat(chemin);
    lignes.push(f + ' | ' + stats.size + ' octets | ' + path.extname(f));
  }
  const sortie = path.join(dossier, 'inventaire.txt');
  await fs.writeFile(sortie, lignes.join('\n'));
  console.log('Inventaire écrit sur', os.platform(), ':', sortie);
}

inventaire('rapports').catch(console.error);

Script perso rapide

Un petit utilitaire ponctuel (renommer, trier des fichiers) : le Sync y est acceptable, le script se termine aussitôt.

Équipe mixte Windows/Linux

Salim bannit les slashs en dur : tout chemin passe par path.join pour tourner partout sans retouche.

Serveur en production

fs.promises + await partout : aucun appel Sync sur le chemin des requêtes, la boucle d'événements reste libre.

Diagnostic machine

Avant de déployer, os.cpus().length et os.freemem() calibrent le dimensionnement (voir clustering, section 5-2).

✓ Bonnes pratiques

  • Importez les natifs par leur nom (préfixe node: possible) sans rien installer.
  • Utilisez fs.promises + await en serveur ; réservez le Sync aux scripts et à la config.
  • Construisez chaque chemin avec path.join, jamais par concaténation.

✗ Pièges fréquents

  • Utiliser les versions Sync sur un serveur (blocage pour tous les clients).
  • Concaténer les chemins à la main (bug de portabilité Windows/Linux).
  • Installer un paquet npm pour ce qu'un natif fait déjà.

Points clés à retenir

  • Les MODULES NATIFS (fs, path, os, http, crypto…) sont intégrés à Node (aucune installation) et donnent accès au système ; importés par leur nom.
  • fs (fichiers) : privilégier l'ASYNCHRONE (fs.promises + await, non-bloquant) en serveur ; le synchrone (Sync) bloque la boucle d'événements (réservé aux scripts/config).
  • path : manipuler les chemins de façon PORTABLE (path.join gère le bon séparateur / vs \) — ne jamais concaténer les chemins à la main.
  • os : infos système (plateforme, os.cpus pour le clustering, mémoire). Les natifs sont le FONDEMENT des bibliothèques (Express repose sur http).

Défi 48h — appliquez demain

Écrivez le script d'inventaire du modèle ci-dessus sur un vrai dossier (vos rapports, vos exports) : recensez nom, taille et extension en asynchrone, avec des chemins path.join. Bannissez tout appel Sync du script.

Checklist sortie de section
  • Je peux citer les 3 natifs et leur rôle en une phrase chacun.
  • Je sais écrire un script fs.promises + await sans Sync.
  • J'ai produit un inventaire réel avec des chemins portables.
Auto-évaluation 1-5 : est-ce que j'utilise l'asynchrone en serveur ?est-ce que je bannis les chemins concaténés à la main ?

Questions fréquentes

Version asynchrone ou synchrone de fs : laquelle utiliser, et quand le synchrone est-il acceptable ?

En serveur, toujours l'asynchrone (fs.promises + await) : Node est mono-thread, et un appel synchrone (readFileSync…) bloque ce thread pendant toute l'opération — le serveur ne répond alors à aucun autre client. C'est exactement ce que le modèle non-bloquant de Node cherche à éviter. Le synchrone n'est acceptable que dans deux cas : les petits scripts ponctuels (pas de serveur, pas d'utilisateurs simultanés) et le chargement de config au démarrage (une fois, avant d'écouter). Règle de Salim : Sync sur le chemin d'une requête = faute de performance.

Pourquoi utiliser le module path plutôt que construire les chemins à la main ?

Parce que la concaténation manuelle casse dès qu'on change de système : le séparateur est / sur macOS/Linux mais \ sur Windows, et on produit vite des doubles séparateurs ou des morceaux collés (« fichier introuvable »). path.join(__dirname, 'data', nom) assemble avec le bon séparateur selon le système et gère les cas limites ; path.resolve() produit un absolu, basename/extname/dirname découpent proprement. Le code de Salim tourne sans retouche sur son Windows et le Linux de l'équipe.

Vérifiez votre compréhension

Salim lit des rapports sur un serveur utilisé par toute l'équipe. Quelle version de fs ?

Ressources supplémentaires (optionnel, pour aller plus loin)

Examen du niveau 2Questions inédites et durcies pour valider vos acquis