backend / nodejs
Express — bases
Explication
Ce que vous allez apprendre
- Créer une application Express avec des routes pour chaque verbe HTTP
- Distinguer
req.params,req.queryetreq.body - Comprendre pourquoi
express.json()doit être monté avant les routes qui lisentreq.body - Organiser des routes par ressource avec
Router() - Chaîner plusieurs handlers sur une même route avec
next()
Dans quel contexte ?
Une API POST /users renvoie systématiquement req.body: undefined alors que le client envoie bien un JSON valide avec les bons headers. Un développeur passe une heure à vérifier le client avant de réaliser que express.json() n'a jamais été ajouté avec app.use() dans le fichier principal. Cette leçon couvre ce piège précis ainsi que l'ensemble des bases d'Express : routage, sources de données d'une requête, et organisation en routers.
Ce qu'Express automatise
La leçon précédente a montré tout le travail manuel nécessaire pour router des requêtes et parser un body avec le module http natif. Express automatise exactement ce travail.
C'est un framework minimaliste qui fournit une API simple et cohérente pour définir des routes. Sans imposer de structure rigide au reste de l'application — c'est pour ça qu'il reste, des années après sa création, l'un des frameworks Node les plus utilisés.
Commençons par une distinction essentielle : une requête transporte trois types d'informations différents. req.params capture les segments d'URL, par exemple /users/:id donne req.params.id.
req.query capture, lui, les paramètres après le ?. /search?q=chat donne req.query.q, utile pour des filtres ou une pagination.
Enfin, req.body contient le contenu envoyé, comme un JSON ou un formulaire. Mais attention : il n'est disponible que si express.json() a été monté AVANT la route concernée — un piège classique pour qui se demande pourquoi req.body reste vide.
| Source | Exemple d'URL | Où la lire |
|---|---|---|
| Path params | /users/:id | req.params.id |
| Query string | /search?q=chat | req.query.q |
| Body (JSON/formulaire) | POST /users avec un JSON | req.body (nécessite express.json()) |
Piège fréquent
Monter express.json() APRÈS une route au lieu d'avant (app.get(...) puis app.use(express.json())) fait que req.body reste undefined pour toute route déclarée avant ce middleware. L'ordre de déclaration dans Express compte toujours.
Une fois ces trois sources maîtrisées, une question d'organisation se pose vite. Sur un petit projet, mettre toutes les routes dans un seul fichier app.js fonctionne, mais dès que l'application grossit, ça devient illisible.
Router() répond exactement à ce problème. Il permet de découper les routes par ressource (utilisateurs, produits, commandes) dans des fichiers séparés, puis de les "brancher" sur un préfixe d'URL dans le fichier principal.
Il reste un dernier point à observer avant la prochaine leçon : le chaînage de handlers. Plusieurs fonctions peuvent traiter une même requête successivement, chacune décidant de passer la main à la suivante via next().
Ce mécanisme n'est qu'un aperçu. La prochaine leçon, dédiée aux middlewares, l'explique en profondeur.
Le piège fréquent à connaître avant de pratiquer : oublier express.json() fait que req.body reste undefined, ce qui provoque des erreurs difficiles à comprendre plus loin dans le code, souvent un plantage sur req.body.nom alors que le vrai problème est en amont.
Commandes & code
Express — bases
npm install express// app.js
import express from "express";
const app = express();
app.use(express.json()); // parse automatiquement les bodies JSON dans req.body
app.get("/", (req, res) => {
res.json({ message: "API en ligne" });
});
app.get("/users/:id", (req, res) => {
const { id } = req.params;
res.json({ id, name: `Utilisateur ${id}` });
});
app.get("/search", (req, res) => {
const { q, page = "1" } = req.query;
res.json({ query: q, page: Number(page) });
});
app.post("/users", (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: "name et email requis" });
}
const user = { id: crypto.randomUUID(), name, email };
res.status(201).json(user);
});
app.listen(3000, () => console.log("API sur http://localhost:3000"));// Tous les verbes HTTP, réponses variées
app.put("/users/:id", (req, res) => {
res.json({ id: req.params.id, ...req.body });
});
app.patch("/users/:id", (req, res) => {
res.json({ id: req.params.id, patched: true });
});
app.delete("/users/:id", (req, res) => {
res.status(204).send(); // pas de contenu
});
app.get("/download", (req, res) => {
res.download("./files/rapport.pdf"); // envoie un fichier en pièce jointe
});
app.get("/redirect-old", (req, res) => {
res.redirect(301, "/new-path");
});// Servir des fichiers statiques
app.use("/public", express.static("public"));
// GET /public/logo.png -> sert ./public/logo.png// Router modulaire — organiser les routes par ressource
// routes/users.js
import { Router } from "express";
const router = Router();
router.get("/", (req, res) => res.json({ users: [] }));
router.get("/:id", (req, res) => res.json({ id: req.params.id }));
router.post("/", (req, res) => res.status(201).json(req.body));
export default router;
// app.js
import usersRouter from "./routes/users.js";
app.use("/users", usersRouter); // toutes les routes préfixées par "/users"// Chaîner plusieurs handlers sur une même route
app.get(
"/admin/stats",
(req, res, next) => {
console.log("Log d'accès à /admin/stats");
next(); // passe au handler suivant
},
(req, res) => {
res.json({ visits: 4231 });
}
);Résumé
express.json()doit être monté avant les routes qui lisentreq.body.req.params,req.queryetreq.bodycouvrent path params, query string et payload JSON.Router()permet de découper l'application en modules de routes par ressource.- Une route peut chaîner plusieurs handlers, exécutés dans l'ordre via
next().
Exercices pratiques
Mission : corriger un req.body fantôme et restructurer une API
Objectif : Diagnostiquer un req.body toujours undefined causé par un ordre de middlewares incorrect, puis découper une API monolithique en routers par ressource.
Contexte
L'endpoint POST /users d'une API Express renvoie systématiquement une erreur 400 "name et email requis", alors que Postman montre bien un JSON valide envoyé avec Content-Type: application/json. En lisant app.js, tu remarques que app.post('/users', ...) est déclaré à la ligne 12, et que app.use(express.json()) n'apparaît qu'à la ligne 40, tout en bas du fichier. Toutes les routes de l'API (users, products, orders) sont actuellement mélangées dans ce seul fichier de 200 lignes.