Retour au cours

backend / nodejs

Express — bases

Leçon 61 exercice

Explication

Ce que vous allez apprendre

  • Créer une application Express avec des routes pour chaque verbe HTTP
  • Distinguer req.params, req.query et req.body
  • Comprendre pourquoi express.json() doit être monté avant les routes qui lisent req.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.

SourceExemple d'URLOù la lire
Path params/users/:idreq.params.id
Query string/search?q=chatreq.query.q
Body (JSON/formulaire)POST /users avec un JSONreq.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

bash
npm install express
js
// 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"));
js
// 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");
});
js
// Servir des fichiers statiques
app.use("/public", express.static("public"));
// GET /public/logo.png -> sert ./public/logo.png
js
// 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"
js
// 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 lisent req.body.
  • req.params, req.query et req.body couvrent 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

1 disponible
1

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.

Résoudre l’exercice →