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)
| Objet | RÎle | Propriétés clés |
|---|---|---|
req | RequĂȘte entrante | method, url, headers |
res | Réponse sortante | writeHead(), 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}`);
});
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éthode | Description |
|---|---|
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 = N | Dé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);
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);
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.