Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 8

Node.js HTTP Server Fundamentals

Recommended resources

Bonjour ! Bienvenue dans cette nouvelle leçon.

Introduction

Dans la leçon précédente, nous avons appris à utiliser npm pour initialiser un projet et gérer ses dépendances. Vous savez maintenant comment créer un package.json, installer des paquets et comprendre la différence entre dependencies et devDependencies.

Aujourd'hui, nous allons mettre cette base en pratique pour construire notre toute première application web avec Node.js. L'objectif est d'apprendre à créer un serveur HTTP simple en utilisant uniquement le module http intégré à Node.js.

Étant donné votre expérience avec Laravel, vous êtes familier avec le concept de requêtes et de réponses web. Cependant, il y a une différence fondamentale dans l'approche. Avec PHP/Laravel, un serveur web externe comme Apache ou Nginx reçoit la requête et la transmet à votre application PHP pour qu'elle la traite. Avec Node.js, votre application est elle-même le serveur web. Elle écoute directement les connexions sur le réseau.

Comprendre ce fonctionnement à bas niveau est essentiel. Cela vous donnera une base solide pour apprécier ce que des frameworks comme Express.js (que nous aborderons dès le prochain module) apportent pour nous simplifier la vie.


1. Le modèle de serveur de Node.js

Avant de coder, il est crucial de comprendre comment Node.js gère les requêtes. Contrairement aux serveurs traditionnels qui créent souvent un nouveau fil d'exécution (thread) pour chaque requête, Node.js utilise un modèle mono-thread (single-threaded) et événementiel (event-driven).

Traditional vs. Node.js Request Handling
Cette image illustre la différence fondamentale : à gauche, un modèle traditionnel où les requêtes peuvent attendre qu'un thread se libère. À droite, le modèle Node.js où une seule boucle d'événements (Event Loop) gère toutes les requêtes de manière non bloquante.

Cela signifie que Node.js peut gérer un grand nombre de connexions simultanées avec une faible consommation de ressources, car il ne bloque jamais une opération. Lorsqu'une tâche longue (comme lire un fichier ou interroger une base de données) est lancée, Node.js la délègue au système d'exploitation et continue de traiter d'autres requêtes. Une fois la tâche terminée, un événement est placé dans une file d'attente et traité par la boucle d'événements (Event Loop) dès que possible.

C'est le rôle du module http de nous donner les outils pour créer un serveur qui s'intègre dans ce modèle.

How To Create a Web Server in Node.js with the HTTP Module

Pour commencer, lisez cette introduction de DigitalOcean. Elle explique ce qu'est un serveur web et le rôle du module http de Node.js.

Lisez les sections "Introduction" et "Understanding the HTTP Module". Concentrez-vous sur les points qui expliquent que le module http est intégré à Node.js (pas d'installation nécessaire) et sur les concepts clés comme les streams et l'absence de routage intégré ("No magic routing").


2. Création d'un serveur "Hello World"

Passons à la pratique. Nous allons créer le serveur le plus simple possible.

Ouvrez votre terminal, créez un nouveau dossier pour ce projet, naviguez dedans et initialisez un projet npm :

mkdir mon-premier-serveur
cd mon-premier-serveur
npm init -y

Créez ensuite un fichier server.js. C'est ici que nous allons écrire le code de notre serveur.

Le processus de création d'un serveur se déroule en trois étapes clés :

  1. Importer le module http : C'est le module qui contient toutes les fonctionnalités pour créer le serveur.
  2. Créer le serveur : En utilisant la fonction http.createServer(). On lui passe une fonction (appelée "request listener") qui sera exécutée à chaque fois qu'une requête HTTP arrive. Cette fonction prend deux arguments : req (la requête entrante) et res (la réponse à envoyer).
  3. Mettre le serveur en écoute : En utilisant la méthode server.listen(), on indique au serveur sur quel port et quelle adresse il doit attendre les requêtes.

Voici le code complet pour server.js :

// 1. Importer le module http
const http = require('http');

// Définir l'hôte et le port
const host = 'localhost';
const port = 8000;

// 2. Créer la fonction "request listener"
// Cette fonction sera exécutée pour chaque requête entrante
const requestListener = function (req, res) {
    // Définir le code de statut HTTP et le type de contenu
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    
    // Envoyer la réponse et fermer la connexion
    res.end("Hello, World!");
};

// 3. Créer le serveur et le mettre en écoute
const server = http.createServer(requestListener);

server.listen(port, host, () => {
    console.log(`Le serveur tourne sur http://${host}:${port}`);
});

Pour une démonstration visuelle de ce processus, la vidéo suivante est excellente.

HTTP Module in Node.js

Regardez cette vidéo de Programming with Mosh. Il explique clairement comment importer le module http, créer le serveur et le mettre en écoute.

Regardez la section de 00:52 à 02:29. Observez comment il utilise http.createServer() et server.listen(). Notez son explication sur le fait que l'objet server est un EventEmitter, ce qui est une caractéristique centrale de Node.js.

Pour lancer votre serveur, exécutez la commande suivante dans votre terminal :

node server.js

Vous devriez voir le message Le serveur tourne sur http://localhost:8000. Ouvrez votre navigateur web et visitez cette adresse. Vous verrez "Hello, World!" s'afficher !

Vous pouvez arrêter le serveur à tout moment en retournant sur votre terminal et en appuyant sur CTRL+C.


3. Anatomie d'une transaction HTTP

Maintenant que notre serveur fonctionne, décortiquons ce qui se passe. Les objets req et res sont au cœur de tout.

  • L'objet request (req) : C'est une instance de IncomingMessage. Il contient toutes les informations sur la requête du client : l'URL (req.url), la méthode HTTP (req.method), les en-têtes (req.headers), et le corps de la requête (pour les requêtes POST/PUT).

  • L'objet response (res) : C'est une instance de ServerResponse. C'est un flux inscriptible (WritableStream) que vous utilisez pour renvoyer des données au client.

    • res.writeHead(statusCode, headers) : Écrit le code de statut et les en-têtes de la réponse. C'est une bonne pratique de toujours définir le Content-Type.
    • res.write(data) : Écrit un morceau de données dans le corps de la réponse.
    • res.end(data) : Termine la réponse. Peut aussi envoyer le dernier morceau de données. Cette méthode est obligatoire pour finaliser la réponse.

Anatomy of an HTTP Transaction

La documentation officielle de Node.js contient un excellent guide qui détaille l'anatomie d'une transaction HTTP. Parcourez-le pour approfondir votre compréhension des objets request et response.

Lisez les sections suivantes : "Method, URL and Headers" pour voir comment accéder aux informations de la requête. "HTTP Status Code" pour comprendre comment définir le code de statut. "Setting Response Headers" et "Explicitly Sending Header Data" pour la gestion des en-têtes. "Sending Response Body" pour voir comment utiliser write() et end().


4. Implémenter un routage de base

Pour l'instant, notre serveur renvoie la même réponse, quelle que soit l'URL demandée. Pour construire une application utile, nous avons besoin de router les requêtes, c'est-à-dire d'exécuter un code différent en fonction de l'URL.

Avec le module http de base, ce routage doit être fait manuellement. C'est ici que vous allez vraiment apprécier ce que fait routes/api.php dans Laravel pour vous.

Modifions notre server.js pour gérer trois routes :

  • GET / : renvoie un message de bienvenue en HTML.
  • GET /api/user : renvoie un objet utilisateur en JSON.
  • Toute autre route : renvoie une erreur 404 "Not Found".
const http = require('http');

const host = 'localhost';
const port = 8000;

const requestListener = function (req, res) {
    if (req.url === "/") {
        // Route principale
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.end("<h1>Page d'accueil</h1><p>Bienvenue sur notre serveur Node.js !</p>");
    } else if (req.url === "/api/user") {
        // Route API
        const user = {
            firstname: "John",
            lastname: "Doe"
        };
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(user));
    } else {
        // Route non trouvée
        res.writeHead(404, { 'Content-Type': 'text/plain' });
        res.end("404 Not Found");
    }
};

const server = http.createServer(requestListener);

server.listen(port, host, () => {
    console.log(`Le serveur tourne sur http://${host}:${port}`);
});

Redémarrez votre serveur (CTRL+C puis node server.js) et testez les différentes URL dans votre navigateur :

  • http://localhost:8000/
  • http://localhost:8000/api/user
  • http://localhost:8000/une-autre-page

Cette approche avec if/else fonctionne mais devient vite ingérable. Une technique un peu plus propre consiste à utiliser un objet pour mapper les routes à des fonctions de gestion (handlers).

NodeJS Server - How to Build Routes without Express

Cette vidéo montre une manière plus structurée d'organiser le routage sans utiliser de framework. C'est une excellente transition vers la mentalité des frameworks comme Express.

Regardez les sections de 01:02 à 12:52. Ne vous préoccupez pas de reproduire le code à l'identique. Concentrez-vous sur l'idée principale : Créer un objet routes où chaque clé est un chemin d'URL et chaque valeur est une fonction. Dans le requestListener, extraire le chemin de l'URL demandée. Utiliser ce chemin pour trouver la fonction correspondante dans l'objet routes et l'exécuter.

Cette approche plus structurée commence déjà à ressembler à un micro-framework. On définit des routes et des "contrôleurs" (les fonctions de handler).


Conclusion

Félicitations ! Vous avez construit et fait tourner un serveur web de A à Z avec Node.js, sans aucune dépendance externe. Vous comprenez maintenant les mécanismes de base qui animent toutes les applications web Node.js.

Points clés à retenir :

  • Le module http de Node.js permet de créer un serveur web directement dans votre code.
  • L'architecture est basée sur http.createServer() qui prend une fonction "request listener", et server.listen() qui démarre le serveur.
  • Le "request listener" ((req, res) => { ... }) est le cœur de votre application, où vous analysez la requête (req) et construisez la réponse (res).
  • Le routage, la gestion des en-têtes et des formats de réponse (HTML, JSON) doivent être gérés manuellement.
  • Cette approche manuelle est puissante mais devient rapidement complexe à mesure que l'application grandit.

Ce que nous avons fait manuellement aujourd'hui (le routage, l'envoi de JSON, la gestion des erreurs 404) est précisément ce que les frameworks web comme Express.js sont conçus pour simplifier.

Dans la prochaine leçon, qui marque le début de notre deuxième module, nous allons mettre en place notre premier projet avec Express.js. Vous verrez immédiatement comment il nous permet de structurer notre code de manière beaucoup plus propre et efficace pour construire des API REST robustes.