Chapitre 03 — Comment les API sont conçues5 min de lecture

Ressources et relations

Un seul tableur ne suffit pas

Dans les leçons précédentes, nous avons examiné des tables uniques : Utilisateurs, Produits. Mais les applications réelles n'ont pas une seule table. Elles en ont des dizaines, parfois des centaines, toutes connectées les unes aux autres.

Pensez à Uber. Il y a une table Utilisateurs, une table Chauffeurs, une table Courses, une table Paiements, une table Avis. Chacune contient des données différentes, mais elles se réfèrent toutes les unes aux autres. Une course appartient à un utilisateur et à un chauffeur. Un paiement appartient à une course. Un avis appartient également à une course.

Ces connexions entre les tables s'appellent des relations. Et c'est la raison pour laquelle votre ingénieur dit parfois : "C'est plus compliqué qu'il n'y paraît".


Les relations, à la manière d'un tableur

Regardons deux tables. D'abord, une table Utilisateurs simple :

idnomemail
1Alice Martinalice@acme.com
2Bob Chenbob@startup.io
3Clara Diazclara@bigcorp.com

Maintenant, une table Commandes :

iduser_idproduittotalstatut
1011Souris sans fil2500livré
1021Hub USB-C4500expédié
1032Lampe de bureau3200livré
1043Clavier8900en cours
1053Support écran5500livré

Voyez-vous la colonne user_id ? C'est le lien. La commande n°101 a un user_id : 1, ce qui signifie qu'elle appartient à Alice (id = 1 dans la table Utilisateurs). La commande n°103 a un user_id : 2, elle appartient donc à Bob.

Si vous utilisez des tableurs, c'est comme un RECHERCHEV (VLOOKUP). La colonne user_id dit : "Va dans la table Utilisateurs et trouve la ligne avec cet ID". C'est ainsi que les bases de données connectent les tables entre elles.

Cette seule relation répond à des tonnes de questions :

  • "Quelles sont les commandes d'Alice ?" → filtrer Commandes où user_id = 1
  • "Qui a passé la commande n°104 ?" → chercher user_id = 3 → Clara
  • "Combien de commandes Bob a-t-il passées ?" → compter les lignes de Commandes où user_id = 2 → une

Les routes imbriquées (Nested routes)

Les conventions REST permettent d'exprimer ces relations dans les URL. On les appelle des routes imbriquées :

GET /users/1/posts

Lisez-le de gauche à droite : "Va dans Utilisateurs, trouve l'utilisateur n°1, puis récupère ses articles (posts)". L'URL reflète la relation dans la base de données.

Essayons. DummyJSON supporte les routes imbriquées. Voici les articles de l'utilisateur n°1 :

GEThttps://dummyjson.com/users/1/posts

Maintenant les tâches (todos) de l'utilisateur n°1 :

GEThttps://dummyjson.com/users/1/todos

Et les commentaires sur l'article n°1 :

GEThttps://dummyjson.com/posts/1/comments

Le modèle est toujours le même : /parent/id/enfants. L'URL vous indique exactement quelle relation vous suivez. Une fois que vous connaissez les noms des ressources, vous pouvez souvent deviner la route imbriquée sans lire la doc.


Pourquoi votre ingénieur dit "c'est compliqué"

Passons à la partie pratique. Vous êtes PM, vous avez une idée de fonctionnalité, et votre ingénieur vous dit : "C'est plus compliqué que vous ne le pensez". Voici trois scénarios qui expliquent pourquoi.

Scénario 1 : "Afficher l'historique des commandes sur le profil utilisateur"

Votre table Utilisateurs et votre table Commandes sont déjà connectées par user_id. La relation existe. La donnée est là. L'ingénieur doit simplement appeler GET /users/1/orders et afficher les résultats.

Verdict : Simple. Le modèle de données le permet déjà.

Scénario 2 : "Permettre aux utilisateurs de mettre des produits en favoris"

Actuellement, vous avez une table Utilisateurs et une table Produits. Mais il n'y a pas de lien entre elles pour les favoris. Où stockez-vous le fait qu'Alice a mis la souris sans fil en favoris ?

Vous ne pouvez pas simplement ajouter une colonne favoris à la table Utilisateurs. Que contiendrait-elle ? Une liste d'ID de produits ? Cela casse le fonctionnement des bases de données (une valeur par cellule, tout comme un tableur).

La réponse : vous avez besoin d'une nouvelle table.

iduser_idproduit_idcree_le
11422024-06-10
21172024-06-11
32422024-06-12
4382024-06-15

Il s'agit d'une table Favoris. Chaque ligne signifie "cet utilisateur a mis ce produit en favoris à cette date". Alice (utilisateur 1) a mis en favoris les produits 42 et 17. Bob (utilisateur 2) a également mis en favoris le produit 42.

Ce modèle est appelé plusieurs-à-plusieurs (many-to-many) : de nombreux utilisateurs peuvent mettre de nombreux produits en favoris, et de nombreux produits peuvent être mis en favoris par de nombreux utilisateurs. Cela nécessite toujours une nouvelle table intermédiaire.

C'est pourquoi l'ingénieur dit que c'est compliqué. Il ne s'agit pas seulement d'afficher des données. Il s'agit de créer une toute nouvelle table, de nouveaux endpoints (GET /users/1/favorites, POST /users/1/favorites, DELETE /users/1/favorites/42) et une nouvelle logique.

Scénario 3 : "Ajouter des tags aux produits"

Cela semble simple, n'est-ce pas ? "Ajoutez juste un champ tags". Mais réfléchissez-y : un produit peut avoir plusieurs tags, et un tag peut appartenir à plusieurs produits. C'est à nouveau du plusieurs-à-plusieurs. Vous avez besoin de :

  • Une table Tags (id, nom)
  • Une table ProductTags (id, produit_id, tag_id)

Deux nouvelles tables pour ce qui semblait être "juste un champ". C'est pourquoi les PM qui comprennent les modèles de données rédigent de meilleures specs et ont des conversations plus productives avec les ingénieurs.


Points clés à retenir

  • Les applications réelles possèdent de nombreuses tables connectées par des références d'ID (comme user_id).
  • Une colonne user_id dans Commandes est comme un RECHERCHEV vers la table Utilisateurs.
  • Les routes imbriquées (/users/1/posts) expriment ces relations dans les URL.
  • Les relations plusieurs-à-plusieurs (favoris, tags) nécessitent toujours une nouvelle table intermédiaire.
  • Quand un ingénieur dit "c'est compliqué", il veut souvent dire que le modèle de données doit changer.