Il y a une tentation, quand on apprend à construire une API, de commencer par la base de données. On dessine ses tables, on les expose une par une, et on appelle ça du REST. Le résultat fonctionne — mais il ne parle pas. Chaque endpoint est une fenêtre sur une table, et c’est au client de recoller les morceaux pour comprendre quoi que ce soit.
Ce guide prend le chemin inverse. On part d’un système réel — un réseau de boîtes à livres — et on remonte des besoins des gens jusqu’au code.
Le principe à retenir : une bonne API ne décrit pas une base de données, elle offre un abstraction permettant de répondre efficacement à des questions.
Lino est un réseau de boîtes à livres en libre-service disséminées dans une ville. Vous en avez probablement croisé : une petite armoire vitrée au coin d’une rue, remplie de romans que des inconnus déposent et prennent librement. Pas de bibliothécaire, pas de carte de membre, pas de date de retour. La confiance est implicite.
Avant d’écrire une seule ligne de code, il faut comprendre ce que les gens font déjà, sans application.
Quelqu’un marche dans son quartier, tombe sur une boîte, feuillette, prend un livre, en laisse un autre. Quelqu’un d’autre cherche un titre précis et fait le tour des boîtes à pied, sans jamais savoir si le livre qu’il veut se trouve à trois rues de là. Un troisième dépose un roman qu’il a adoré en espérant secrètement que quelqu’un le trouvera.
L’application ne doit pas remplacer ces gestes. Elle doit les amplifier — rendre visible ce qui était invisible, sans alourdir ce qui marchait déjà. C’est une contrainte de conception avant d’être une contrainte technique.
Quand on conçoit à partir des tables, on se demande « quelles entités ai-je à stocker? ». Quand on conçoit à partir des usages, on se demande « quelles questions les gens se posent? ». Les deux produisent des systèmes très différents.
Voici les questions que pose un utilisateur de Lino :
Où sont les boîtes près de moi, et est-ce qu’elles valent le détour?
Qu’est-ce qu’il y a dans cette boîte devant laquelle je suis?
Est-ce que quelqu’un, quelque part, a le livre que je cherche?
Je viens de déposer un livre — qui pourrait le vouloir?
D’où vient ce livre que je tiens? Combien de mains l’ont eu avant moi?
Chacune de ces questions deviendra un flux — une séquence d’échanges entre le client et le serveur. Et c’est là le cœur de l’affaire : un protocole n’est pas une liste d’endpoints, c’est une chorégraphie. Un endpoint isolé répond à une question; une séquence d’endpoints raconte une interaction.
Chaque flux ci-dessous se lit à trois niveaux. Commencez par le scénario — ce qu’un humain veut accomplir. Passez ensuite à la correspondance REST — comment ce besoin se traduit en ressources et en méthodes. Terminez par le flux intégré — la séquence complète, avec le code et les réponses.
01
Trouver des boîtes à proximité
Clara — citoyenne
Voir les boîtes autour de soi, avec assez d'information pour décider de s'y rendre ou non.
Clara ouvre l’application en marchant dans le Plateau. Elle veut savoir s’il y a
des boîtes à livres autour d’elle — et si elles contiennent quelque chose d’intéressant.
Elle ne veut pas se déplacer pour rien.
L’app utilise sa position GPS et affiche les boîtes proches avec leur distance,
leur activité récente, et une estimation du contenu.
Correspondance REST
Ressource
/boites — les boîtes à livres
Action
GET — lire, pas modifier
Filtrage
?lat=45.523&lon=-73.587&rayon=2km
Route Express
router.get(“/boites”, handler)
La réponse inclut la distance calculée et l’activité récente — le client
n’a pas à faire ces calculs lui-même.
Flux intégré
GET /boites?lat=45.523&lon=-73.587&rayon=2km
La position GPS est envoyée à l’ouverture de l’app.
Le serveur interroge MongoDB avec un index 2dsphere, calcule les distances et trie du plus proche au plus loin.
Réponse 200 :
[{
"id": "boite-14",
"nom": "Boîte du parc Laurier",
"distance": "450m",
"derniere_activite": "il y a 2 heures",
"livres_estimes": 12,
"_links": { "detail": "/boites/boite-14" }
}]
Clara voit la boîte à 450m, active — elle s’y rend. Clic → GET /boites/boite-14
Remarquez ce que la réponse fait : elle ne renvoie pas des coordonnées brutes à charge pour Clara de calculer une distance. Elle renvoie "450m", "il y a 2 heures", "12 livres estimés" — de l’information déjà interprétée. La réponse fait le travail à la place du client. C’est ce qui sépare une API qui parle d’une API qui déverse.
02
Déposer un livre et prévenir ceux qui le cherchent
Marc — donneurSystèmeSophie — chercheuse
Marc signale un dépôt. Sophie veut être prévenue quand le livre qu'elle cherche apparaît.
Marc est devant la boîte du parc Laurier. Il dépose un exemplaire de Dune.
Quelque part dans le quartier, Sophie cherche ce livre depuis deux semaines.
Marc scanne le code QR et confirme le dépôt. Le système vérifie si quelqu’un
cherche ce titre. Sophie, la prochaine fois qu’elle consulte sa recherche,
voit que Dune a été trouvé à 450m de chez elle.
Correspondance REST
Dépôt
POST /boites/:id/livres
Alerte
POST /recherches
Consultation
GET /recherches/:id
Effet de bord
Le POST de Marc met à jour la recherche de Sophie — sans qu’elle agisse
Flux intégré
Sophie crée son alerte (avant) :
POST /recherches → 201 { id: "rech-5", statut: "en attente" }
Marc dépose :
POST /boites/boite-14/livres avec { titre, code_qr }
Le serveur insère l’événement, met à jour la boîte, et cherche les recherches actives pour « Dune »
Réponse 201 avec recherches_notifiees: 1
Sophie revient (après) :
GET /recherches/rech-5 — même endpoint, réponse différente
Ce flux cache quelque chose d’important. Le POST de Marc ne se contente pas d’enregistrer un livre — il modifie l’état d’une autre ressource : la recherche de Sophie. Un seul appel déclenche une cascade côté serveur. Et la notification de Sophie n’arrive pas par magie : elle doit revenir consulter sa recherche. C’est du polling, et c’est exactement là où le modèle requête-réponse montre ses limites. Sophie aimerait être prévenue, mais REST ne sait pas pousser — il sait seulement répondre quand on demande. On y reviendra.
03
Suivre le parcours d'un livre
Léa — curieuse
Voir l'histoire d'un livre : son parcours à travers les boîtes de la ville.
Léa a trouvé un livre avec un code QR sur la couverture. Elle le scanne par
curiosité : qui l’a lu avant elle? D’où vient-il? Combien de boîtes a-t-il traversées?
L’app affiche le parcours complet : chaque boîte où il est passé, les dates,
la distance totale parcourue.
Correspondance REST
Ressource
/livres/:code_qr
Action
GET — lecture seule
Données
Agrégation des événements (dépôts + retraits), pas un document stocké tel quel
C’est un endpoint sémantique : il ne retourne pas une ligne de base de données,
il raconte une histoire en agrégeant des événements.
Léa voit que le livre a voyagé 3.2km. Elle décide de le lire et de le redéposer ailleurs.
Cet endpoint est le plus révélateur des trois. Il n’y a aucune table « parcours » dans la base. Le parcours n’existe pas comme donnée — il est calculé à la volée à partir d’une suite d’événements bruts (des dépôts et des retraits). L’endpoint prend des faits dispersés et en fait un récit. C’est ce qu’on appelle une vue dérivée : la donnée stockée et la donnée servie ne sont pas la même chose.
Les livres dans une boîte pourraient être stockés dans le document de la boîte (un tableau livres). Mais chaque livre a un cycle de vie indépendant — il traverse plusieurs boîtes. Si les livres sont imbriqués, le parcours d’un livre (GET /livres/QR-7890) demande de chercher dans tous les documents boîtes.
En séparant les événements, le parcours d’un livre est une simple requête :
Les livres actuellement dans une boîte se déduisent des événements : les dépôts sans retrait correspondant. C’est un calcul, pas un stockage — et c’est un choix de conception (stocker l’état courant vs le recalculer à partir de l’historique).
Tout ce qu’on a vu dans le cours est là, condensé : la validation avant l’action, les codes de statut qui portent un verdict (400, 404, 201), les erreurs expressives qui disent au client quoi corriger, l’effet de bord sur une autre ressource, et le champ _links qui guide le client vers la prochaine étape sans qu’il ait à deviner les URLs.
Quelques lectures qui prolongent la réflexion sur la conception d’API — moins sur la syntaxe, plus sur les intentions derrière les choix.
RESTful API Design: nouns are good, verbs are bad (Google Cloud) — le billet classique sur la différence entre nommer des ressources et nommer des actions, et pourquoi le premier produit des API prévisibles.
REST APIs must be hypertext-driven (Roy Fielding) — l’inventeur de REST explique, avec une certaine exaspération, ce que la plupart des « API REST » ratent : l’hypermédia comme moteur de l’état. Court, tranchant, fondateur.
Roy Fielding’s Misappropriated REST Dissertation (Two-Bit History) — une histoire fascinante de comment le mot « REST » a dérivé de sa définition d’origine. Utile pour comprendre pourquoi tant de débats sur « est-ce vraiment du REST » tournent en rond.
REST, Explained for Beginners (htmx) — une relecture du chapitre 5 de la thèse de Fielding pour des développeurs non-académiques. Défend l’idée que l’hypermédia (HTML, justement) est plus proche de l’esprit REST que les API JSON modernes.
Web API Design Best Practices (Microsoft Azure) — un guide complet et pragmatique sur la modélisation des ressources, le filtrage, la pagination et le versionnage. Une bonne référence à garder sous la main.
Most RESTful APIs aren’t really RESTful (Florian Krämer) — une discussion accessible de ce que le découplage client-serveur via l’hypermédia apporte réellement, au-delà du débat « verbes ou noms ».
L’idée centrale à retenir est qu’une API n’est pas simplement une copie de votre base de données rendue accessible en ligne. Une API est une interface : elle définit une façon claire et contrôlée d’interagir avec votre système. Son rôle est de permettre à d’autres développeurs, applications ou services de comprendre ce qu’ils peuvent demander à votre système et comment l’utiliser, sans avoir besoin d’entrer dans les détails de son code ou de sa structure interne.