Node.js est un environnement d'exécution qui fait tourner du JavaScript en dehors du navigateur, directement sur le serveur. Il s'appuie sur le moteur V8 de Chrome pour compiler le JavaScript en code natif, et l'entoure de bibliothèques permettant d'accéder au réseau, aux fichiers et au système. Résultat : un même langage, du front-end au back-end.

Le modèle événementiel non bloquant

La particularité de Node.js tient à son architecture mono-thread, asynchrone et non bloquante. Plutôt que de dédier un thread à chaque requête, Node.js confie les opérations lentes (accès disque, réseau, base de données) au système, et poursuit son exécution. Quand l'opération se termine, une fonction de rappel est placée dans la file d'attente et traitée par la boucle d'événements (event loop).

const fs = require('fs');

console.log('1 — début');

fs.readFile('./data.txt', 'utf8', (err, contenu) => {
  if (err) return console.error(err);
  console.log('3 — fichier lu :', contenu.length, 'caractères');
});

console.log('2 — suite immédiate');
// Ordre d'affichage : 1, 2, puis 3 quand la lecture est terminée.

Ce modèle rend Node.js particulièrement efficace pour les applications fortement liées aux entrées/sorties — API, serveurs temps réel, passerelles — où l'on attend beaucoup le réseau ou le disque. À l'inverse, un calcul lourd et purement processeur bloque la boucle : on le délègue alors à un worker thread ou à un service séparé.

npm et le fichier package.json

Node.js est livré avec npm, son gestionnaire de paquets. Le fichier package.json décrit le projet : ses dépendances, ses scripts et ses métadonnées.

npm init -y                 # crée un package.json minimal
npm install express         # ajoute une dépendance (bloc "dependencies")
npm install --save-dev jest # dépendance de développement uniquement
npm run start               # exécute le script "start"
{
  "name": "mon-service",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node server.js",
    "dev": "node --watch server.js",
    "test": "jest"
  },
  "dependencies": {
    "express": "^4.19.0"
  }
}

Le dossier node_modules/ (les dépendances installées) ne se versionne pas ; c'est package-lock.json qui fige les versions exactes pour des installations reproductibles.

Modules : ESM et CommonJS

Node.js supporte deux systèmes de modules. CommonJS, historique, utilise require et module.exports. ESM (ECMAScript Modules), le standard moderne, utilise import et export — c'est celui à privilégier pour un nouveau projet.

// CommonJS (historique)
const http = require('http');
module.exports = { maFonction };

// ESM (moderne) — nécessite "type": "module" dans package.json
import http from 'node:http';
export { maFonction };

Le préfixe node: (comme node:http, node:fs) désigne explicitement un module intégré, sans ambiguïté avec un paquet npm du même nom.

Un premier serveur HTTP

Sans aucune dépendance, le module intégré http suffit à répondre à des requêtes.

import http from 'node:http';

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
  res.end(JSON.stringify({ message: 'Bonjour depuis Node.js', url: req.url }));
});

server.listen(3000, () => {
  console.log('Serveur à l\'écoute sur http://localhost:3000');
});

En pratique, on s'appuie souvent sur un cadriciel comme Express ou Fastify, qui simplifient le routage, la gestion des erreurs et les intergiciels (middlewares).

import express from 'express';

const app = express();
app.use(express.json());

app.get('/api/salut', (req, res) => {
  res.json({ message: 'Bonjour' });
});

app.listen(3000, () => console.log('Express prêt sur le port 3000'));

Asynchrone : callbacks, promesses, async/await

L'écriture asynchrone a beaucoup évolué. Les callbacks imbriqués ont laissé place aux promesses, puis à la syntaxe async/await, aujourd'hui la plus lisible.

import { readFile } from 'node:fs/promises';

async function lireConfig() {
  try {
    const brut = await readFile('./config.json', 'utf8');
    return JSON.parse(brut);
  } catch (err) {
    console.error('Lecture impossible :', err.message);
    return null;
  }
}

Les commandes essentielles

Commande Rôle
node fichier.js Exécute un script
node --watch fichier.js Relance automatiquement à chaque modification
node -v Affiche la version de Node.js
npm init -y Crée un package.json par défaut
npm install Installe toutes les dépendances du projet
npm install <paquet> Ajoute une dépendance
npm run <script> Exécute un script défini dans package.json
npm outdated Liste les dépendances à mettre à jour
npm audit Signale les vulnérabilités connues
npx <outil> Exécute un outil sans l'installer globalement

Quand choisir Node.js

Node.js brille pour les API REST et GraphQL, les serveurs temps réel (WebSocket, chat, notifications), les passerelles et micro-services, ainsi que l'outillage front-end (build, bundlers). On le réserve moins aux traitements intensifs en calcul, où un langage compilé ou un service dédié reste plus adapté.

Sa force, comme pour l'écosystème JavaScript en général, tient à l'unité du langage : les mêmes compétences servent du navigateur au serveur, et npm met à disposition l'un des plus vastes catalogues de bibliothèques open source.