2.4 · Créer un serveur HTTP natif

Niveau 2 · Cœur de Node.js

2.4Créer un serveur HTTP natif

Objectif : Créer un serveur HTTP avec le module natif (req/res, routage manuel, codes et en-têtes HTTP) et comprendre le socle sur lequel Express est bâti.
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

  • Un serveur en quelques lignes : createServer((req, res) => ...) puis .listen(port) — chaque requête déclenche la fonction.
  • req lit, res répond : méthode, url et en-têtes d'un côté ; statut, en-têtes et corps de l'autre — le vocabulaire HTTP des API.
  • À la main, puis Express : router par if et parser le corps montre ce qu'Express simplifie — jamais de boîte noire.

Mini-cas filé — Salim sert l'inventaire dans le navigateur

Dernier acte : Salim veut que l'équipe consulte l'inventaire (2.1) depuis le navigateur, sans installer quoi que ce soit. Il crée un serveur avec le module http : une route qui liste les rapports en JSON, une qui affiche l'accueil en HTML, un 404 pour le reste — le tout routé à la main selon req.method et req.url. En galérant sur le parsing du corps et la cascade de if, il comprend exactement ce qu'Express (niveau 3) va lui simplifier. Le chantier du niveau 2 est livré : fichiers, événements, streams, serveur.

Épilogue du fil rouge 2.1-2.3 : l'outillage de Salim devient un serveur consultable par l'équipe.

1. Un serveur web sans dépendance

Node crée des serveurs web et des API avec son module natif http, sans aucune dépendance — et le faire « à la main » AVANT Express est très formateur. On crée un serveur avec http.createServer() en lui passant une fonction appelée à CHAQUE requête : elle reçoit req (request : méthode GET/POST, URL, en-têtes, corps) et res (response : ce qu'on construit et renvoie). Puis .listen(port) fait écouter les connexions (ex. 3000). Quelques lignes pour un serveur fonctionnel : Salim affiche l'inventaire dans le navigateur de l'équipe avec une dizaine de lignes.

2. req, res et le vocabulaire HTTP

Côté requête, req fournit req.method (GET, POST, PUT, DELETE…), req.url (le chemin, ex. /rapports), req.headers — et pour un POST, le corps se lit comme un stream (section 2-3), morceau par morceau. Côté réponse, res construit : res.statusCode = 200, res.setHeader('Content-Type', 'application/json'), res.write(...) puis res.end(...). Il faut connaître HTTP : les méthodes (GET lire, POST créer, PUT/PATCH modifier, DELETE supprimer — le vocabulaire des API REST, section 4-1), les codes (200 OK, 201 Created, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error…), les en-têtes (type de contenu, authentification, cache…).

3. Tout à la main — puis Express

En natif, on gère TOUT à la main : router selon URL et méthode (cascade de if), parser le corps, définir les en-têtes, gérer les erreurs — vite fastidieux pour une vraie application. C'est là qu'Express (section 3-1) intervient : routage élégant, parsing du corps, middlewares — mais tout repose SUR le module http. Créer un serveur natif est donc un exercice fondateur : il révèle le socle (req/res, HTTP, ports) et fait apprécier ce qu'un framework apporte. On n'écrira pas de serveurs natifs en production (on utilisera Express), mais cette compréhension fait la différence entre utiliser Express en le comprenant et le subir en boîte noire — y compris pour déboguer quand la « magie » échoue.

Erreur d'interprétation fréquente : « Puisque je vais utiliser Express, je peux sauter le serveur natif ». Non. Sans le natif, Express est une magie : app.get() « marche » sans qu'on sache qu'une requête arrive, qu'il faut router selon méthode et URL, définir statut et en-têtes, envoyer un corps. En ayant routé à la main et parsé un corps en stream, on MESURE ce qu'Express simplifie — et quand un en-tête manque ou un corps n'est pas parsé, on sait diagnostiquer au lieu d'être démuni. Quelques lignes de natif évitent des mois de boîte noire : Salim a routé trois chemins à la main avant de toucher à Express.
Le cycle d'une requête Du client à la réponse Client Navigateur :GET /rapportsServeur createServer : fonctionappelée à chaque requêteRoutage if surreq.method + req.urlRéponse res : statut,en-têtes, corps, end()
Du client à la réponse
Version texte du schéma (SEO / lecteurs d'écran / traduction) : 1) Le CLIENT envoie une requête : méthode + chemin + en-têtes (ex. GET /rapports). 2) Le SERVEUR de Salim la reçoit : http appelle sa fonction avec (req, res) — le serveur est un EventEmitter qui émet à chaque requête. 3) Il ROUTE à la main : des if sur req.method et req.url choisissent la réponse (liste JSON, accueil HTML, 404). 4) Il RÉPOND avec res : statusCode, Content-Type, corps via end(). Leçon : ce cycle manuel est le socle — Express l'automatise sans le remplacer.

Le saviez-vous ?

Salim a montré son serveur « dix lignes » à l'équipe — et son lead a cru qu'il avait installé un framework.
Vrai : le module http natif suffit à servir une page et une API, sans rien installer.

Dix lignes de natif valent mieux qu'un framework subi.

🔎 Exemple concret — 🔎 Exemple concret — Le serveur d'inventaire de Salim

Trois chemins routés à la main, comme chez Salim :

GET /          → 200, page d'accueil HTML
GET /rapports  → 200, inventaire en JSON (Content-Type: application/json)
Autre chemin   → 404, message d'erreur
POST /rapports → 201 après lecture du corps en stream

Lecture : chaque combinaison méthode + URL a sa réponse et son statut.

Leçon : on mesure ce qu'Express simplifiera (routage, parsing).

Vocabulaire de la section

http (module natif)
Module créant un serveur web sans dépendance : http.createServer() avec une fonction appelée à chaque requête, puis .listen(port).

Exemple simple : Salim sert l'inventaire sur le port 3000 avec dix lignes.

Ce que ce n'est pas : un framework comme Express (qui repose dessus).

Réflexe : comprendre le socle avant d'utiliser le framework.

req (request)
Objet de la requête entrante : méthode (req.method), chemin (req.url), en-têtes (req.headers), corps lu comme un stream pour POST.

Exemple simple : Salim route selon req.method et req.url.

Ce que ce n'est pas : l'objet avec lequel on construit la réponse (c'est res).

Réflexe : lire la demande = req.

res (response)
Objet de construction de la réponse : statut (res.statusCode), en-têtes (res.setHeader), corps (res.write/res.end).

Exemple simple : Salim renvoie res.statusCode = 200 puis res.end(JSON.stringify(...)).

Ce que ce n'est pas : la requête envoyée par le client (c'est req).

Réflexe : construire la réponse = res, et toujours terminer par end().

Méthodes & codes HTTP
Méthodes : GET (lire), POST (créer), PUT/PATCH (modifier), DELETE (supprimer). Codes : 200 OK, 201 Created, 400/401/404 (client), 500 (serveur).

Exemple simple : GET /rapports → 200, POST /rapports → 201, chemin inconnu → 404.

Ce que ce n'est pas : un 200 renvoyé pour tout, succès comme erreurs.

Réflexe : chaque réponse = le bon verbe + le bon code.

Ce qu'Express simplifie
Le natif gère tout à la main (routage par if, parsing du corps, en-têtes) ; Express fournit routage, parsing, middlewares — sur le module http.

Exemple simple : Salim remplace sa cascade de if par app.get('/rapports', ...) au niveau 3.

Ce que ce n'est pas : un serveur concurrent qui remplace http.

Réflexe : fastidieux à la main = besoin couvert par Express.

En pratique — En pratique — Créer un serveur HTTP natif

  1. Créez un serveur avec http.createServer((req, res) => {...}) et démarrez-le avec .listen(3000) ; testez dans le navigateur (localhost:3000).
  2. Renvoyez une réponse : définissez res.statusCode, res.setHeader('Content-Type', ...) et terminez avec res.end('Hello').
  3. Routez « à la main » selon req.method et req.url (une cascade de if) : renvoyez des réponses différentes selon le chemin.
  4. Renvoyez du JSON (Content-Type application/json + res.end(JSON.stringify(data))) et constatez combien gérer tout à la main devient vite fastidieux.
Vous créez un serveur HTTP avec le module natif (req/res, routage manuel, codes et en-têtes HTTP) et comprenez le socle sur lequel Express est bâti — le « sous le capot ».

Script prêt à dire — défendre l'exercice natif avant Express (30 s)

« Avant Express, j'ai routé trois chemins à la main : je sais ce qu'est une requête, comment on choisit la réponse selon la méthode et l'URL, et pourquoi chaque statut compte. Maintenant, quand j'écrirai app.get, je saurai ce qu'Express fait pour moi — et quand ça cassera, je saurai où regarder. »

À dire en montrant le serveur natif qui tourne sur localhost:3000 ; la démo dans le navigateur convainc.

Modèle copier-coller — serveur natif avec routage manuel

const http = require('node:http');

const rapports = [
  { nom: 'rapport-01.txt', taille: 1240 },
  { nom: 'rapport-02.txt', taille: 986 }
];

const serveur = http.createServer((req, res) => {
  if (req.method === 'GET' && req.url === '/') {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/html; charset=utf-8');
    res.end('

Inventaire des rapports

'); } else if (req.method === 'GET' && req.url === '/rapports') { res.statusCode = 200; res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.end(JSON.stringify(rapports)); } else { res.statusCode = 404; res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.end('Non trouvé'); } }); serveur.listen(3000, () => console.log('Serveur sur http://localhost:3000'));

Démo locale

localhost:3000 devant l'équipe : l'inventaire de Salim s'affiche dans le navigateur, zéro déploiement.

API JSON d'équipe

GET /rapports renvoie l'inventaire en JSON : les collègues le consomment depuis leurs propres scripts.

Gros fichier à servir

Envoyer l'export de 2 Go : on pipe un createReadStream vers res (section 2.3) au lieu de tout charger.

Vers Express

Dès que les if s'empilent : app.get('/rapports', ...) remplace la cascade — même socle http dessous.

✓ Bonnes pratiques

  • Testez chaque route dans le navigateur ou avec curl (méthode + URL + statut).
  • Renvoyez toujours le bon statut (201 après création, 404 si inconnu, 500 en cas d'erreur).
  • Terminez chaque réponse par res.end(), sinon le client attend indéfiniment.

✗ Pièges fréquents

  • Sauter le natif et subir Express en boîte noire.
  • Renvoyer 200 pour tout, succès comme erreurs.
  • Oublier res.end() (requête qui pend indéfiniment).

Points clés à retenir

  • Node crée des serveurs web avec le module natif http (sans dépendance) : http.createServer((req, res) => ...) puis .listen(port). Formateur AVANT Express.
  • req = la requête (méthode, url, headers, corps en stream) ; res = la réponse (statusCode, setHeader, write/end).
  • Connaître HTTP : MÉTHODES (GET/POST/PUT/DELETE) et CODES DE STATUT (200, 201, 400, 401, 404, 500) — le vocabulaire des API REST.
  • Le serveur natif gère TOUT à la main (routage par if, parsing du corps, en-têtes) → vite fastidieux ; Express le simplifie mais REPOSE sur http.

Défi 48h — appliquez demain

Mettez en ligne le serveur du modèle avec vos vraies données (votre inventaire 2.1) : accueil HTML, route JSON, 404 soignée. Montrez-le à un collègue dans son navigateur, puis notez en une phrase ce qu'Express vous simplifiera.

Checklist sortie de section
  • Je peux expliquer le cycle requête → routage → réponse.
  • Je sais servir du HTML et du JSON avec les bons statuts.
  • Un collègue a consulté mon serveur natif dans son navigateur.
Auto-évaluation 1-5 : est-ce que je maîtrise req/res et les statuts ?est-ce que je comprends ce qu'Express fait pour moi ?

Questions fréquentes

Pourquoi apprendre le serveur HTTP natif si je vais utiliser Express de toute façon ?

Pour utiliser Express en comprenant au lieu de le subir. En ayant routé à la main (cascade de if), parsé un corps en stream et défini les en-têtes, vous SAVEZ ce qui se passe vraiment quand vous écrivez app.get() — et vous MESUREZ ce qu'Express simplifie (routage élégant, express.json(), middlewares). Surtout, vous déboguez : en-tête manquant, statut incorrect, corps non parsé — vous savez regarder au bon niveau au lieu d'être démuni quand la « magie » échoue. Comme Salim : trois chemins à la main avant le premier app.get.

Quelles sont les bases du protocole HTTP que je dois absolument connaître ?

Le cycle requête → réponse, les MÉTHODES (GET lire, POST créer, PUT remplacer, PATCH modifier partiellement, DELETE supprimer — une même URL fait des choses différentes selon la méthode), les CODES DE STATUT (2xx succès : 200 OK, 201 Created ; 4xx erreur client : 400, 401, 403, 404 ; 5xx erreur serveur : 500 — une bonne API renvoie le bon code, pas 200 pour tout), et les EN-TÊTES (Content-Type, authentification, cache). Ce vocabulaire structure les API REST (section 4-1) et se pratique dès le serveur natif : chaque route de Salim renvoie méthode, statut et Content-Type corrects.

Vérifiez votre compréhension

Un chemin inconnu arrive sur le serveur natif de Salim. Que doit-il renvoyer ?

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

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