1. http.createServer

Le module http est un module natif de Node.js — pas d'installation npm nĂ©cessaire. Il permet de crĂ©er un serveur HTTP complet en quelques lignes.

Imports et création du serveur

const http = require('http'); // Module natif Node.js

// Créer un serveur HTTP
// Le callback est appelĂ© Ă  CHAQUE requĂȘte entrante
const server = http.createServer((req, res) => {
  // req = IncomingMessage  — donnĂ©es de la requĂȘte
  // res = ServerResponse   — pour construire la rĂ©ponse
  res.end('Hello depuis Node.js !');
});

// Démarrer le serveur sur le port 3000
server.listen(3000, () => {
  console.log('Serveur démarré sur http://localhost:3000');
});

IncomingMessage (req) et ServerResponse (res)

ObjetRÎlePropriétés clés
reqRequĂȘte entrantemethod, url, headers
resRéponse sortantewriteHead(), write(), end()

Serveur complet avec gestion d'erreur

const http = require('http');

const server = http.createServer((req, res) => {
  console.log(`${req.method} ${req.url}`); // Logger chaque requĂȘte

  res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
  res.end('Bonjour le monde !');
});

// Gestion des erreurs serveur
server.on('error', (err) => {
  console.error('Erreur serveur :', err.message);
  if (err.code === 'EADDRINUSE') {
    console.error('Le port est déjà utilisé !');
    process.exit(1);
  }
});

server.listen(3000, '127.0.0.1', () => {
  const addr = server.address();
  console.log(`Serveur écoutant sur http://${addr.address}:${addr.port}`);
});
💡 Le callback de createServer est un gestionnaire d'Ă©vĂ©nements enregistrĂ© sur l'Ă©vĂ©nement request. Vous pouvez aussi Ă©crire server.on('request', handler).

2. req — Analyser la requĂȘte

L'objet req (IncomingMessage) contient toutes les informations sur la requĂȘte HTTP entrante : mĂ©thode, URL, headers, et body (en chunks).

Propriétés essentielles de req

const http = require('http');

http.createServer((req, res) => {
  // Méthode HTTP : GET, POST, PUT, DELETE, PATCH, OPTIONS...
  console.log('Méthode   :', req.method);      // "GET"

  // URL complĂšte avec query string
  console.log('URL       :', req.url);          // "/api/users?page=2"

  // Headers de la requĂȘte
  console.log('User-Agent:', req.headers['user-agent']);
  console.log('Content-Type:', req.headers['content-type']);

  // HTTP version
  console.log('HTTP      :', req.httpVersion); // "1.1"

  res.end('OK');
}).listen(3000);

Parser l'URL avec new URL()

const http = require('http');

http.createServer((req, res) => {
  // new URL() nécessite une base (l'hÎte)
  const url = new URL(req.url, `http://${req.headers.host}`);

  console.log('Pathname :', url.pathname);      // "/api/users"
  console.log('Search   :', url.search);        // "?page=2&limit=10"

  // URLSearchParams pour lire les query params
  const page  = url.searchParams.get('page')  ?? '1';
  const limit = url.searchParams.get('limit') ?? '10';
  console.log(`Page: ${page}, Limite: ${limit}`);

  res.end(`Page ${page} — ${limit} rĂ©sultats`);
}).listen(3000);

Logger toutes les requĂȘtes

const http = require('http');

http.createServer((req, res) => {
  const timestamp = new Date().toISOString();
  const url = new URL(req.url, `http://localhost`);

  console.log(`[${timestamp}] ${req.method} ${url.pathname}`);

  // Afficher tous les headers
  console.log('Headers:');
  for (const [key, value] of Object.entries(req.headers)) {
    console.log(`  ${key}: ${value}`);
  }

  res.end('RequĂȘte reçue et loggĂ©e');
}).listen(3000, () => {
  console.log('Logger actif sur http://localhost:3000');
});
💡 req.headers est un objet JavaScript standard. Les noms de headers sont toujours en minuscules dans Node.js, quelle que soit la casse envoyĂ©e par le client.

3. res — Construire la rĂ©ponse

L'objet res (ServerResponse) permet de construire et envoyer la réponse HTTP : status code, headers, et body.

Méthodes principales de res

MéthodeDescription
res.writeHead(code, headers)Définit le status code et les headers
res.setHeader(name, value)Définit un header individuel
res.write(data)Écrit une partie du body (streaming)
res.end(data?)Termine et envoie la réponse
res.statusCode = NDéfinit le status code (propriété)

Réponse JSON structurée

const http = require('http');

http.createServer((req, res) => {
  // Définir les headers AVANT d'envoyer le body
  res.writeHead(200, {
    'Content-Type': 'application/json; charset=utf-8',
    'Access-Control-Allow-Origin': '*',
    'X-Powered-By': 'Node.js',
  });

  const data = {
    success: true,
    message: 'Bienvenue sur l\'API',
    version: '1.0.0',
    timestamp: new Date().toISOString(),
  };

  // JSON.stringify() convertit l'objet en chaĂźne JSON
  res.end(JSON.stringify(data));
}).listen(3000);

Différents Content-Types

const http = require('http');

http.createServer((req, res) => {
  const url = new URL(req.url, 'http://localhost');

  if (url.pathname === '/json') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ type: 'JSON', ok: true }));

  } else if (url.pathname === '/html') {
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.end('

Bonjour depuis Node.js !

En HTML natif.

'); } else if (url.pathname === '/text') { res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }); res.end('Réponse en texte brut'); } else { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Route non trouvée' })); } }).listen(3000);
⚠ res.writeHead() doit ĂȘtre appelĂ© avant tout res.write() ou res.end(). AprĂšs l'envoi des headers, il est impossible de les modifier.

4. Routing manuel

Sans framework, le routing consiste à comparer req.method et req.url pour déterminer quel code exécuter.

Routing if/else basique

const http = require('http');

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
];

http.createServer((req, res) => {
  const url = new URL(req.url, 'http://localhost');
  const { pathname } = url;

  res.setHeader('Content-Type', 'application/json');

  // GET /
  if (req.method === 'GET' && pathname === '/') {
    return res.end(JSON.stringify({ message: 'API Node.js', version: '1.0' }));
  }

  // GET /users
  if (req.method === 'GET' && pathname === '/users') {
    return res.end(JSON.stringify({ data: users }));
  }

  // GET /users/:id — avec RegExp
  const matchUser = pathname.match(/^\/users\/(\d+)$/);
  if (req.method === 'GET' && matchUser) {
    const user = users.find(u => u.id === parseInt(matchUser[1]));
    if (user) return res.end(JSON.stringify({ data: user }));
    res.writeHead(404);
    return res.end(JSON.stringify({ error: 'Utilisateur non trouvé' }));
  }

  // 404 pour toutes les autres routes
  res.writeHead(404);
  res.end(JSON.stringify({ error: 'Route non trouvée', url: pathname }));
}).listen(3000, () => console.log('Serveur sur http://localhost:3000'));

Classe Router réutilisable

class Router {
  constructor() {
    this.routes = [];
  }

  // Ajouter une route
  add(method, path, handler) {
    // path peut ĂȘtre une string exacte ou un RegExp
    this.routes.push({ method: method.toUpperCase(), path, handler });
  }

  // Raccourcis
  get(path, handler)    { this.add('GET',    path, handler); }
  post(path, handler)   { this.add('POST',   path, handler); }
  put(path, handler)    { this.add('PUT',    path, handler); }
  delete(path, handler) { this.add('DELETE', path, handler); }

  // Dispatcher une requĂȘte
  dispatch(req, res) {
    const url = new URL(req.url, 'http://localhost');
    for (const route of this.routes) {
      if (route.method !== req.method) continue;
      const match = (route.path instanceof RegExp)
        ? url.pathname.match(route.path)
        : url.pathname === route.path;
      if (match) {
        req.params = match.groups ?? {};
        return route.handler(req, res);
      }
    }
    res.writeHead(404, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'Route non trouvée' }));
  }
}

// Utilisation
const router = new Router();
router.get('/', (req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({ message: 'Accueil' }));
});
router.get(/^\/users\/(?<id>\d+)$/, (req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({ userId: req.params.id }));
});

const http = require('http');
http.createServer((req, res) => router.dispatch(req, res)).listen(3000);
💡 Dans la pratique, on utilise Express (module N05) plutĂŽt que d'implĂ©menter un router manuellement. Comprendre cette mĂ©canique vous aide Ă  apprĂ©cier ce qu'Express apporte.

5. Lire le body POST

Contrairement aux headers, le body d'une requĂȘte n'est pas disponible immĂ©diatement. Il arrive en chunks (morceaux) via des Ă©vĂ©nements. Il faut les accumuler puis les traiter.

Lire le body en événements

const http = require('http');

http.createServer((req, res) => {
  // Accumuler les chunks
  let rawBody = '';

  req.on('data', (chunk) => {
    // chunk est un Buffer — .toString() le convertit en string
    rawBody += chunk.toString();

    // Sécurité : limiter la taille du body
    if (rawBody.length > 1e6) { // 1 MB max
      req.socket.destroy(); // Couper la connexion
    }
  });

  req.on('end', () => {
    // Tout le body est reçu
    try {
      const body = JSON.parse(rawBody);
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ received: body }));
    } catch (e) {
      res.writeHead(400, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: 'JSON invalide' }));
    }
  });

  req.on('error', (err) => {
    console.error('Erreur lecture body:', err);
    res.writeHead(500);
    res.end('Erreur serveur');
  });
}).listen(3000);

Promesse readBody réutilisable

// Utilitaire — transformer la lecture du body en Promise
function readBody(req, maxBytes = 1_000_000) {
  return new Promise((resolve, reject) => {
    let data = '';

    req.on('data', (chunk) => {
      data += chunk.toString();
      if (data.length > maxBytes) {
        reject(new Error('Payload trop grand (max 1 MB)'));
        req.socket.destroy();
      }
    });

    req.on('end', () => {
      try {
        // Si le body est vide, retourner un objet vide
        resolve(data.trim() ? JSON.parse(data) : {});
      } catch {
        reject(new Error('Body JSON invalide'));
      }
    });

    req.on('error', reject);
  });
}

// Usage dans le serveur
const http = require('http');

http.createServer(async (req, res) => {
  res.setHeader('Content-Type', 'application/json');

  if (req.method === 'POST' && req.url === '/echo') {
    try {
      const body = await readBody(req);
      res.writeHead(200);
      res.end(JSON.stringify({ success: true, data: body }));
    } catch (e) {
      res.writeHead(400);
      res.end(JSON.stringify({ success: false, error: e.message }));
    }
    return;
  }

  res.writeHead(404);
  res.end(JSON.stringify({ error: 'Route non trouvée' }));
}).listen(3000, () => console.log('POST /echo disponible'));
💡 async/await fonctionne parfaitement avec le callback de createServer. Les erreurs non capturĂ©es dans un handler async doivent ĂȘtre gĂ©rĂ©es avec try/catch pour Ă©viter des crashes silencieux.
← Accueil Exercices →