Conception des ressources et des points de terminaison
- Leçon 7
- 35 min
- Module 3 : Concevoir un contrat utilisable
Leçon 7 sur 21 · Créer et déployer votre première APIConception des ressources et des points de terminaison
1. Comprendre la requête Web
2. Créer un petit service FastAPI
3. Concevoir un contrat utilisable
4. Documenter l'API avec OpenAPI et Swagger UI
5. Explorer et tester avec Postman
6. Tester et renforcer le service
7. Déployer et vérifier
Ce que vous saurez faire
Donnez à l’API des routes qu’une personne qui ne la connaît pas peut deviner. Donnez-lui aussi une version pour pouvoir la modifier sans briser les programmes qui l’utilisent déjà. Ensuite, écrivez le contrat : chaque requête et la réponse exacte en cas de réussite ou de chaque échec.
Avant de commencerCe qu’il faut savoir et avoir sous la main
Ce que vous devez savoir
- Les méthodes et les paramètres de chemin et de requête, présentés dans Les bases de HTTP : méthodes et Les bases de HTTP : paramètres de chemin et paramètres de requête.
- Quel module contient les routes, les modèles et le classificateur, présenté dans Séparer les responsabilités : trois responsabilités, trois modules.
Ce qu’il vous faut sur votre ordinateur
- Le paquet
ticket_apiavecmain.py,models.py,routes.pyetclassifier.py. Vous les avez créés dans Séparer les responsabilités : exercice guidé : diviser le projet. Si vous avez ajoutérefunddans la tâche « À vous de jouer », annulez les deux changements, comme indiqué. requests/billing.json, créé dans Modèles de requête et de réponse : exercice guidé : créer et appeler le point de terminaison.- Le serveur de développement fonctionne, comme dans Environnement et premier point de terminaison : étape 3 : démarrer le serveur.
- Si votre projet manque ou ne fonctionne plus, copiez l’instantané
end-of-m02, le projet à la fin du module 2. Utiliser un instantané explique les étapes.
Testé avec Python 3.14.6 et FastAPI 0.142.2 sur macOS.
Une API est une promesse
Section intitulée « Une API est une promesse »Quand un autre programme utilise votre API, son code dépend de vos chemins, noms de champs et codes de statut. Si vous renommez category en team, ce programme ne fonctionne plus. Vous ignorez peut-être même son existence. Ensemble, les chemins, champs et codes de statut forment le contrat de l’API. C’est une promesse à chaque client (un programme qui appelle l’API).
Concevez donc le contrat de façon réfléchie, avant que de nombreux clients en dépendent. Cette leçon présente quatre règles.
Règle 1 : les chemins nomment les ressources
Section intitulée « Règle 1 : les chemins nomment les ressources »Une ressource est une chose que l’API offre : des billets de soutien, des catégories, une classification. Un chemin REST nomme la ressource avec un nom, et la méthode indique quoi en faire :
| Au lieu de | Écrivez | Pourquoi |
|---|---|---|
GET /getCategories |
GET /v1/categories |
La méthode dit déjà « obtenir ». |
POST /tickets/delete/T-1 |
DELETE /v1/tickets/T-1 |
La méthode indique l’action. |
GET /ticket/T-1 |
GET /v1/tickets/T-1 |
Les collections sont au pluriel : /tickets désigne « tous » et /tickets/T-1 désigne « un seul ». |
Un point de terminaison de cette API, /classify, est un verbe. C’est une exception acceptée : la classification est une action qui ne crée rien. Un nom comme /classifications laisserait croire que l’API les conserve. Choisissez le nom le plus clair, puis gardez-le.
Règle 2 : un seul style de nommage partout
Section intitulée « Règle 2 : un seul style de nommage partout »Choisissez un style une fois, puis utilisez-le dans chaque chemin et chaque champ :
- Chemins : lettres minuscules, noms au pluriel,
-entre les mots :/v1/ticket-categories. - Champs JSON : lettres minuscules avec
_entre les mots :model_version. C’est le style Python, donc les modèles n’ont besoin d’aucune traduction. - Valeurs qu’un programme compare : mots fixes en minuscules :
"billing", pas"Billing".
Une personne qui développe un client peut alors deviner les autres points de terminaison après en avoir vu un.
Règle 3 : une version dans le chemin
Section intitulée « Règle 3 : une version dans le chemin »Certains changements brisent les clients. D’autres non :
| Incompatible : un client peut échouer | Compatible : les anciens clients continuent de fonctionner |
|---|---|
| Renommer ou supprimer un champ, un chemin ou une valeur | Ajouter un point de terminaison |
| Rendre obligatoire un champ de requête facultatif | Ajouter un champ de requête facultatif |
| Changer le type ou le sens d’un champ | Ajouter un champ à une réponse |
| Renvoyer un code de statut différent pour le même cas | Accepter plus de valeurs qu’avant |
Pour un changement incompatible, publiez une nouvelle version de l’API. Gardez l’ancienne en service jusqu’à ce que ses clients passent à la nouvelle. Le plus simple est d’ajouter une version au début du chemin : /v1/classify maintenant, /v2/classify plus tard. Dans chaque URL, les clients voient quelle version ils utilisent.
/health reste sans version. Il concerne le service en cours d’exécution, pas le contrat. Les outils d’hébergement s’attendent à le trouver à un endroit fixe.
Règle 4 : écrire le contrat sous forme de tableau
Section intitulée « Règle 4 : écrire le contrat sous forme de tableau »Un tableau de contrat énumère les requêtes importantes de chaque point de terminaison et la réponse exacte pour chacune. Il sert de plan pour le code, la documentation et les tests. Voici le tableau du point de terminaison de classification, avec le format d’erreur que vous créerez dans la prochaine leçon :
| Requête | Statut | Corps de la réponse |
|---|---|---|
POST /v1/classify {"subject": "Charged twice for March"} |
200 |
{"category": "billing", "priority": 1, "confidence": 0.7, "model_version": "keywords-1.0"} |
{"subject": "Hello"} (aucun mot-clé) |
200 |
{"category": "other", "priority": 3, ...} |
{"subject": ""} |
422 |
{"error": {"code": "invalid_request", "fields": ["subject"], ...}} |
Aucun champ subject |
422 |
Même structure, "fields": ["subject"] |
Un champ absent du modèle, comme "colour" |
422 |
Même structure, "fields": ["colour"] |
| Corps qui n’est pas du JSON valide | 422 |
Même structure, "fields": ["body"] |
GET /v1/classify |
405 |
{"error": {"code": "method_not_allowed", ...}} |
Écrire d’abord les lignes d’échec est une bonne habitude. La plupart des bogues et des questions des clients les concernent.
Exemple détaillé : un préfixe de version et une deuxième ressource
Section intitulée « Exemple détaillé : un préfixe de version et une deuxième ressource »Amira ajoute la version à chaque route de routes.py avec un seul changement : le routeur reçoit un préfixe. Elle ajoute aussi une deuxième ressource, la liste des catégories. Un client peut ainsi les afficher dans un menu sans les copier de la documentation :
from typing import Annotated, get_args
from ticket_api.models import Category, CategoryList, Classification, TicketIn
router = APIRouter(prefix="/v1", tags=["tickets"])
@router.get("/categories")def list_categories() -> CategoryList: return CategoryList(categories=list(get_args(Category)))class CategoryList(BaseModel): categories: list[Category]prefix="/v1" place /v1 devant chaque chemin de ce routeur : /classify devient /v1/classify. tags regroupe les routes dans la documentation (module 4). get_args(Category) donne les valeurs du type Literal. La liste correspond donc toujours au modèle de réponse : il n’y a qu’un endroit à modifier.
La réponse est un objet avec un champ, {"categories": [...]}, et non une simple liste. On pourra ainsi ajouter un champ, comme une description pour chaque catégorie, sans changement incompatible.
curl http://127.0.0.1:8000/v1/categories{"categories":["billing","login","shipping","account","other"]}L’ancien chemin n’existe plus :
curl -X POST http://127.0.0.1:8000/classify -H "Content-Type: application/json" --data @requests/billing.json{"detail":"Not Found"}Exercice guidé : ajouter une version à l’API
Section intitulée « Exercice guidé : ajouter une version à l’API »- Modifiez
routes.pyetmodels.pycomme dans l’exemple détaillé. Enregistrez. - Appelez
GET /v1/categories. Vous voyez les cinq catégories. - Envoyez
requests/billing.jsonà/v1/classify. Le résultat est le même qu’avant. - Envoyez-le à l’ancien chemin
/classify. Vous obtenez404. - Appelez
GET /health. Il fonctionne toujours, sans/v1.
À vous de jouer : le tableau de contrat des catégories
Section intitulée « À vous de jouer : le tableau de contrat des catégories »Écrivez le tableau de contrat de GET /v1/categories, sous la même forme que le tableau ci-dessus. Incluez au moins une ligne d’échec. Puis vérifiez chaque ligne avec curl.
Afficher une réponse
| Requête | Statut | Corps de la réponse |
|---|---|---|
GET /v1/categories |
200 |
{"categories": ["billing", "login", "shipping", "account", "other"]} |
GET /v1/categories?lang=fr (un paramètre de requête inconnu) |
200 |
Le même. Les paramètres de requête inconnus sont ignorés. |
POST /v1/categories |
405 |
{"error": {"code": "method_not_allowed", ...}} après la prochaine leçon. Maintenant : {"detail":"Method Not Allowed"} |
GET /v1/category (singulier) |
404 |
{"error": {"code": "not_found", ...}} après la prochaine leçon. Maintenant : {"detail":"Not Found"} |
Le tableau révèle un écart : actuellement, la structure des corps d’erreur diffère de celle que promet le contrat. La prochaine leçon corrige cet écart.
Erreur courante
Un petit changement de nom qui brise un client
Symptôme : après un « petit nettoyage », une équipe cliente signale que son programme affiche une erreur ou des valeurs vides. Pourtant, votre API « fonctionne » : chaque requête reçoit 200.
Cause : le nettoyage a renommé un champ, par exemple model_version en version, ou une valeur, par exemple "login" en "sign-in". La réponse est valide pour votre nouveau modèle, mais le client lit encore l’ancien nom et ne reçoit rien. Renommer est un changement incompatible, même quand rien n’échoue de votre côté.
Solution :
- Rétablissez l’ancien nom maintenant pour que les clients fonctionnent à nouveau.
- Si le nouveau nom est vraiment nécessaire, ajoutez-le à côté de l’ancien et informez les clients. Supprimez l’ancien seulement dans la prochaine version (
/v2). - Au module 4, vous ajouterez un test qui échoue quand le contrat change. Cette erreur ne pourra donc pas atteindre les clients sans être détectée.
Vérifiez votre compréhension
Quels changements à /v1/classify sont incompatibles? (a) Ajouter un champ de requête facultatif lang. (b) Rendre body obligatoire. (c) Ajouter un champ de réponse explanation. (d) Renvoyer 400 au lieu de 422 pour un subject vide.
Afficher la réponse
(b) et (d) sont incompatibles. (b) : les clients qui n’envoient pas body reçoivent maintenant une erreur. (d) : les clients qui vérifient 422 ne détectent plus l’erreur. (a) et (c) sont compatibles : les anciens clients n’envoient pas lang et ne lisent pas explanation. Ils continuent donc de fonctionner. Les changements incompatibles vont dans /v2.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Récapitulatif
- Les chemins, champs et codes de statut forment un contrat avec chaque client. Concevez-le de façon réfléchie.
- Les chemins nomment les ressources avec des noms au pluriel. La méthode indique quoi faire.
- Gardez un seul style de nommage pour les chemins, les champs et les valeurs.
- Placez une version dans le chemin (
/v1). Un changement incompatible reçoit une nouvelle version. Un ajout n’en reçoit pas. - Écrivez un tableau de contrat en commençant par les lignes d’échec.
Ensuite, vous donnerez la même structure à chaque erreur. Vous veillerez aussi à ce qu’aucune erreur ne révèle le fonctionnement interne du serveur.
Leçon suivante : Validation et réponses d'erreur