1.3 · Modules : CommonJS (require) vs ES Modules (import)

Niveau 1 · JavaScript & bases Node

1.3Modules : CommonJS (require) vs ES Modules (import)

Objectif : organiser le code en modules et comprendre les deux systèmes.
Temps estimé : 11 min

Aucun programme sérieux ne tient dans un seul fichier : on découpe le code en modules — des fichiers séparés, chacun avec une responsabilité, qui s'importent les uns les autres. C'est essentiel pour l'organisation, la réutilisation et la maintenabilité. Node a deux systèmes de modules qui coexistent, et il faut comprendre les deux. Le premier, historique et longtemps par défaut en Node : CommonJS (CJS). On EXPORTE avec module.exports (module.exports = maFonction; ou module.exports = { fn1, fn2 };) et on IMPORTE avec require() (const truc = require('./monModule');). C'est synchrone, simple, et vous le verrez partout dans l'écosystème Node (des millions de paquets npm l'utilisent). Le chemin d'un module local commence par ./ ou ../ ; un module de node_modules (paquet npm) ou natif s'importe par son nom (require('express'), require('fs')).

Le second système : ES Modules (ESM) — la syntaxe de modules standard du langage JavaScript (la même que dans le navigateur moderne). On EXPORTE avec export (export function fn() {}, export default maFonction;) et on IMPORTE avec import (import truc from './monModule.js';, import { fn } from './autre.js';). C'est la direction d'avenir (le standard officiel), et Node le supporte pleinement. Pour utiliser ESM en Node, on l'active soit en nommant ses fichiers .mjs, soit — plus courant — en ajoutant "type": "module" dans le package.json (alors les .js sont traités comme ESM ; à l'inverse, sans cela, ils sont CommonJS par défaut). Quel système choisir ? Pour un NOUVEAU projet, ESM est recommandé (c'est le standard, l'avenir, cohérent avec le front-end). Mais CommonJS reste omniprésent (énormément de code et de paquets existants), donc vous devez comprendre les DEUX : lire du require/module.exports et écrire du import/export. Attention à quelques différences : ESM est asynchrone au chargement, plus strict (chemins de fichiers explicites avec extension), et mélanger les deux dans un projet demande quelques précautions (on peut importer du CommonJS depuis de l'ESM, l'inverse est plus délicat). Ce guide privilégiera une syntaxe claire ; l'essentiel est de comprendre le PRINCIPE (découper en modules, exporter/importer) qui est le même, au-delà de la syntaxe (require vs import). Bien organiser son code en modules — chacun avec une responsabilité claire, exportant ce qui doit l'être — est une compétence fondamentale : c'est ce qui distingue un script bricolé d'une application structurée et maintenable.

Vocabulaire de la section

Module
Fichier séparé avec une responsabilité, qui exporte ce qu'il rend disponible et importe ce dont il a besoin ; base de l'organisation du code.
CommonJS (CJS)
Système de modules historique de Node : export avec `module.exports`, import avec `require()`. Synchrone, simple, omniprésent dans l'écosystème npm.
ES Modules (ESM)
Système de modules standard du langage : export avec `export`, import avec `import`. La direction d'avenir ; activé via .mjs ou "type":"module" dans package.json.
require vs import
require() (CommonJS) et import (ESM) sont les deux syntaxes d'import ; comprendre les deux car les deux coexistent dans le code réel.
Chemin de module
Local : commence par ./ ou ../ (fichiers du projet). Nom seul : module natif (fs) ou paquet npm de node_modules (express).
Vérifiez votre compréhension

Pourquoi découper son code en MODULES ?

Tutoriel 1.3
Tutos « 1.3 » Node.js module CommonJS require ESM import export (recherche)
Cliquer pour voir les résultats à jour ↗

En pratique — Organiser son code en modules

  1. Créez un module qui exporte des fonctions : en CommonJS (`module.exports = { fn1, fn2 }`) puis en ESM (`export function fn1() {}`).
  2. Importez-les depuis un autre fichier : `require('./monModule')` (CJS) et `import { fn1 } from './monModule.js'` (ESM).
  3. Activez ESM dans un projet en ajoutant `"type": "module"` au package.json, et observez que les .js deviennent des ES Modules.
  4. Importez un module natif (fs) et un paquet npm (par leur nom) vs un module local (par ./chemin) — repérez la différence de chemin.
Vous découpez votre code en modules et maîtrisez les deux systèmes (CommonJS require/module.exports et ESM import/export), en comprenant le principe commun d'organisation.

Points clés à retenir

  • Découper le code en MODULES (fichiers avec une responsabilité, qui s'exportent/importent) = fondamental pour l'organisation, la réutilisation, la maintenabilité.
  • COMMONJS (historique, omniprésent) : export `module.exports`, import `require()`. Synchrone, dans des millions de paquets npm.
  • ES MODULES (standard du langage, l'avenir) : export `export`, import `import` ; activé via `.mjs` ou `"type":"module"` dans package.json.
  • Comprendre les DEUX (require ET import coexistent dans le code réel) ; ESM recommandé pour un nouveau projet. Le PRINCIPE (exporter/importer) prime sur la syntaxe.

Questions fréquentes

CommonJS ou ES Modules : lequel choisir pour mon projet, et pourquoi les deux existent-ils ?

Les deux systèmes existent pour des raisons historiques, et le choix pour un nouveau projet penche vers ESM (le standard), mais CommonJS reste incontournable à connaître — clarifions cette coexistence qui déroute beaucoup de débutants. Pourquoi DEUX systèmes ? C'est une question d'histoire. Quand Node est né (2009), JavaScript n'avait PAS de système de modules officiel (le langage n'en prévoyait pas). Node a donc créé le sien : CommonJS (require/module.exports), qui est devenu le standard de facto de tout l'écosystème Node — des millions de paquets npm l'utilisent. Des années plus tard (ES6/2015), le langage JavaScript a enfin standardisé SON propre système de modules : les ES Modules (import/export), utilisés dans les navigateurs modernes. Node a progressivement adopté ce standard officiel, tout en gardant le support de CommonJS (pour ne pas casser l'écosystème existant). D'où la coexistence : CommonJS (l'historique de Node, omniprésent) et ES Modules (le standard officiel du langage, l'avenir). Le choix pour un NOUVEAU projet : (1) ES Modules est recommandé, car : c'est le STANDARD officiel du langage (pas une invention spécifique à Node) ; c'est cohérent avec le front-end (même syntaxe import/export dans le navigateur, React, etc. — un seul système à connaître pour full-stack) ; c'est la direction d'AVENIR (l'écosystème migre progressivement vers ESM) ; la syntaxe est plus moderne et standardisée. (2) Mais CommonJS reste un choix valable, notamment si : vous travaillez avec beaucoup de code/paquets anciens ; vous préférez sa simplicité (chargement synchrone, moins de subtilités) ; votre équipe/projet existant l'utilise déjà (cohérence). Pour APPRENDRE, il faut connaître les DEUX : (1) vous LIREZ énormément de CommonJS (l'immense majorité du code Node existant, tutoriels, paquets, exemples l'utilisent — require est partout) ; (2) vous verrez de plus en plus d'ESM (les nouveaux projets, les bibliothèques modernes) ; (3) vous devez donc comprendre require/module.exports ET import/export. La bonne nouvelle : le PRINCIPE est identique (exporter ce qu'un module rend disponible, importer ce dont on a besoin) ; seule la syntaxe diffère. Une fois le concept de module compris, passer de l'une à l'autre est simple. Les points de vigilance sur la coexistence : (1) Activation d'ESM : par défaut, Node traite les .js comme CommonJS. Pour ESM, il faut soit des fichiers .mjs, soit "type": "module" dans package.json (alors les .js sont ESM, et pour du CommonJS ponctuel on utilise .cjs). (2) Mélange : on peut importer du CommonJS depuis de l'ESM (courant, car beaucoup de paquets sont en CJS), mais l'inverse (require d'un module ESM) est plus délicat. (3) Différences techniques : ESM charge de façon asynchrone, exige les extensions de fichier explicites dans les imports locaux (./x.js pas ./x), est en mode strict, et n'a pas __dirname/require nativement (des équivalents existent). (4) Cohérence : dans UN projet, tenez-vous à un système (tout ESM ou tout CJS) autant que possible, pour éviter les frictions. Recommandation pratique : (1) pour un nouveau projet d'apprentissage, ESM (le standard, l'avenir, cohérent avec le front) est un bon choix — activez "type": "module" ; (2) MAIS soyez à l'aise avec CommonJS car vous le rencontrerez constamment (paquets, exemples, code existant) ; (3) concentrez-vous sur le PRINCIPE (organiser en modules) qui transcende la syntaxe. Ne vous bloquez pas sur ce choix : les deux fonctionnent, se ressemblent conceptuellement, et l'important est de savoir découper votre code en modules propres — la compétence réelle, au-delà de require vs import.

Comment bien organiser mon code en modules, au-delà de la syntaxe ?

Bien organiser son code en modules est une compétence d'architecture qui distingue un script bricolé d'une application maintenable — et elle repose sur des principes indépendants de la syntaxe (require ou import). Voici comment structurer proprement. Le principe fondamental : chaque module a UNE responsabilité claire. Un module ne doit pas être un fourre-tout, mais avoir un rôle précis : gérer les utilisateurs, formuler les requêtes de base de données, définir les routes, contenir des utilitaires de validation, etc. Cette « séparation des responsabilités » rend le code compréhensible (on sait où trouver quoi), réutilisable (un module bien découpé se réutilise) et maintenable (on modifie un module sans casser les autres). Les bonnes pratiques d'organisation : (1) Découpez par fonctionnalité/responsabilité, pas au hasard. Regroupez ce qui va ensemble (toute la logique utilisateur dans un module/dossier user), séparez ce qui est distinct. Une structure typique d'application (qu'on approfondira avec le MVC, section 4-1) sépare : les routes (les URL), les contrôleurs (la logique de traitement), les modèles (les données), les services (la logique métier), les utilitaires. (2) Exportez une interface claire : un module exporte ce que les autres doivent utiliser (ses fonctions/objets publics), et garde le reste privé (interne au module). N'exportez que ce qui est nécessaire — un module avec une interface claire et minimale est plus facile à utiliser et à faire évoluer. (3) Nommez clairement : le nom d'un fichier/module doit dire ce qu'il fait (userService.js, validation.js, db.js). Un nommage clair est une documentation gratuite. (4) Évitez les dépendances circulaires : un module A qui importe B qui importe A crée des problèmes (comme en conception logicielle générale). Organisez les dépendances de façon hiérarchique (les modules de bas niveau — utilitaires, config — sont importés par les modules de haut niveau, pas l'inverse). (5) Un dossier par domaine sur les gros projets : regroupez les modules liés dans des dossiers (un dossier users/ avec ses routes, contrôleur, modèle) pour une organisation lisible à grande échelle. (6) Gérez la configuration séparément : centralisez la config (variables d'environnement, constantes) dans des modules dédiés, importés là où nécessaire (plutôt que des valeurs éparpillées). (7) Modules « minces » et cohérents : évitez les modules géants (difficiles à comprendre) et les modules trop petits/fragmentés (dépendances éparpillées) — trouvez le bon grain (un module = une responsabilité cohérente, de taille raisonnable). Les bénéfices d'une bonne modularité : (1) Compréhension : on navigue et on comprend le code facilement (chaque module a un rôle clair). (2) Réutilisation : un module bien conçu se réutilise dans le projet ou ailleurs. (3) Maintenance : on modifie/corrige un module sans effet de bord sur le reste (couplage faible). (4) Collaboration : plusieurs personnes travaillent sur des modules différents sans se marcher dessus. (5) Testabilité : un module isolé, avec une interface claire, se teste facilement (section 4-4). Les erreurs à éviter : (1) le « God module » (un fichier géant qui fait tout) ; (2) les responsabilités mélangées (un module qui gère à la fois les routes, la base et la validation) ; (3) les dépendances désordonnées (tout importe tout) ; (4) les exports excessifs (exposer des détails internes qui devraient rester privés). Une image : organiser son code en modules, c'est comme ranger une maison — chaque chose à sa place, dans la bonne pièce, facile à retrouver. Un code modulaire bien pensé est agréable à faire évoluer ; un code monolithique et désordonné devient vite un cauchemar. Cette compétence (penser en responsabilités, découper proprement, gérer les dépendances) est fondamentale et transversale (elle vaut au-delà de Node) — investissez-y de l'attention dès vos premiers projets. La syntaxe (require/import) est le « comment » ; l'organisation (quelles responsabilités, quels modules, quelles dépendances) est le « quoi » et le « pourquoi », bien plus important. Un bon développeur pense d'abord à la structure, ensuite à la syntaxe.

Autres ressources