Garder la documentation exacte
- Leçon 12
- 45 min
- Module 4 : Documenter l'API avec OpenAPI et Swagger UI
Leçon 12 sur 21 · Créer et déployer votre première APIGarder la documentation exacte
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
Empêchez toute modification accidentelle du contrat de l’API. Un test compare l’API à une copie révisée de son contrat. Un deuxième test envoie l’exemple documenté. Si l’un des deux ne correspond plus, le test échoue avant qu’un client voie le changement.
Avant de commencerCe qu’il faut savoir et avoir sous la main
Ce que vous devez savoir
- Comment écrire et exécuter des tests pytest, dans Des tests ciblés : de assert à un test.
- Les exemples documentés, dans Une documentation utilisable : exemple détaillé : les modèles.
Ce qu’il vous faut sur votre ordinateur
- L’API documentée, dans Une documentation utilisable : exercice guidé : documenter l’API.
requirements.txtetrequirements-lock.txtdans le projet. Vous les avez créés à l’étape 4 de Environnement et premier point de terminaison : étape 1 : créer le projet. Dans cette leçon, vous y ajoutez les outils de test.- 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-m03, le projet à la fin du module 3, comme dans Utiliser un instantané. Refaites ensuite Une documentation utilisable : exercice guidé : documenter l’API.
Testé avec Python 3.14.6, FastAPI 0.142.2, pytest 9.1.1 et httpx2 2.13.1 sur macOS.
Deux façons dont la documentation pose problème
Section intitulée « Deux façons dont la documentation pose problème »Le code génère la documentation. Elle change donc quand le code change. C’est utile, sauf quand le changement est une erreur :
- Le contrat change sans que personne l’ait décidé. Un « petit nettoyage » renomme
model_versionenversion. La documentation montre le nouveau nom et reste exacte. Mais tous les clients lisent encore l’ancien nom et cessent de fonctionner, comme dans Conception des ressources et des points de terminaison. La documentation était exacte, mais le changement était quand même une erreur. - La documentation et le comportement ne concordent pas. Par exemple, l’API rejette un exemple. Autre cas : le code modifie une réponse dans un gestionnaire sans en informer FastAPI, comme pour la structure
422dans ce module.
Un test peut détecter les deux problèmes. Pour le premier, conservez une copie révisée du contrat dans le projet et comparez l’API à cette copie. Pour le deuxième, envoyez les exemples documentés et comparez les résultats.
Installer les outils de test
Section intitulée « Installer les outils de test »-
Ajoutez les outils pour travailler sur l’API à
requirements.txt:requirements.txt # Needed to run the APIfastapi[standard]==0.142.2# Needed to work on the APIpytest==9.1.1httpx2==2.13.1Le client de test de FastAPI envoie des requêtes à l’application sans serveur. Il utilise le paquet
httpx2pour cela. -
Installez-les et mettez à jour le fichier de verrouillage :
Fenêtre de terminal python -m pip install -r requirements.txtpython -m pip freeze > requirements-lock.txt -
Créez les dossiers
testsetcontract.
Exemple détaillé : le contrat révisé
Section intitulée « Exemple détaillé : le contrat révisé »Un petit module écrit le document OpenAPI actuel dans contract/openapi.json. Il le met en forme pour faciliter la lecture et la comparaison :
"""Write the current OpenAPI document to contract/openapi.json."""
import jsonfrom pathlib import Path
from ticket_api.main import app
path = Path("contract/openapi.json")path.parent.mkdir(exist_ok=True)path.write_text(json.dumps(app.openapi(), indent=2) + "\n", encoding="utf-8")print(f"Wrote {path}")app.openapi() renvoie le même document que /openapi.json, sous forme de dictionnaire. Amira l’exécute une fois. Elle lit le fichier pour vérifier qu’il décrit ce qu’elle veut, puis le conserve dans le projet :
python -m ticket_api.export_contractWrote contract/openapi.jsonDésormais, contract/openapi.json est le contrat qu’elle a révisé. Si elle utilise Git, elle en fait un commit (enregistrement des changements). Chaque modification apparaît dans une demande de tirage (pull request), où une personne peut la réviser.
Puis, deux tests :
import jsonfrom pathlib import Path
from fastapi.testclient import TestClient
from ticket_api.main import app
CONTRACT = Path(__file__).parent.parent / "contract" / "openapi.json"client = TestClient(app)
def test_openapi_matches_the_reviewed_contract(): current = json.dumps(client.get("/openapi.json").json(), indent=2) + "\n" reviewed = CONTRACT.read_text(encoding="utf-8") assert current == reviewed, ( "The API contract changed. If the change is intended, run " "python -m ticket_api.export_contract and review the difference." )
def test_documented_example_gives_the_documented_result(): schemas = client.get("/openapi.json").json()["components"]["schemas"] request_example = schemas["TicketIn"]["examples"][0] response_example = schemas["Classification"]["examples"][0] response = client.post("/v1/classify", json=request_example) assert response.status_code == 200 assert response.json() == response_example| Partie | Raison |
|---|---|
TestClient(app) |
Envoie des requêtes à l’application en mémoire, sans serveur ni port. Le module 6 l’utilise pour tous les tests des points de terminaison. |
json.dumps(..., indent=2) |
Compare les documents comme du texte mis en forme. Un échec montre ainsi les lignes modifiées, plutôt que deux longs dictionnaires. |
Le message dans assert |
Indique quoi faire si le changement est voulu. Un échec de test doit expliquer sa cause. |
| Le deuxième test | Prend les exemples dans le document lui-même. Il vérifie donc exactement ce qu’un développeur voit dans /docs. |
python -m pytesttests/test_contract.py .. [100%]
============================== 2 passed in 0.14s ===============================Détecter un changement accidentel
Section intitulée « Détecter un changement accidentel »Pour vérifier le test, Amira fait le « petit nettoyage ». Dans models.py, elle renomme model_version en version dans Classification et dans son exemple. Dans routes.py, elle remplace model_version=classifier.version par version=classifier.version. L’API fonctionne encore et /docs est exact. Le test échoue :
E AssertionError: The API contract changed. If the change is intended, run python -m ticket_api.export_contract and review the difference.E Skipping 4118 identical leading characters in diff, use -v to showE - "model_version": {E ? ------E + "version": {E "type": "string",...FAILED tests/test_contract.py::test_openapi_matches_the_reviewed_contract - A...========================= 1 failed, 1 passed in 0.13s ==========================La ligne - représente le contrat révisé et la ligne + représente l’API actuelle. Le renommage est visible. Comme il brise la compatibilité, elle l’annule.
Quand un changement est voulu, par exemple un nouveau champ facultatif, voici les étapes :
- Exécutez
python -m ticket_api.export_contractpour écrire le nouveau contrat. - Lisez les différences. Avec Git :
git diff contract/openapi.json. Sans Git, comparez le fichier à votre dernière copie dans VS Code (File › Compare Active File With…, fichier › comparer le fichier actif avec…). - Décidez s’il brise la compatibilité (module 3). Si oui, il faut une nouvelle version, pas seulement un nouveau fichier.
- Relancez les tests. Ils réussissent et le contrat révisé correspond à l’API.
Décider quoi exposer en production
Section intitulée « Décider quoi exposer en production »Les pages de documentation sont utiles à toute personne qui crée un client. Elles montrent aussi chaque point de terminaison et chaque champ à un attaquant. La décision dépend du public de l’API :
| Public de l’API | Documentation en production |
|---|---|
| Le grand public ou de nombreux développeurs externes | Gardez /docs et /openapi.json publics. Ce sont le mode d’emploi du produit. |
| Votre propre interface utilisateur ou quelques équipes connues | Désactivez les pages et donnez plutôt contract/openapi.json aux équipes. |
| Seulement d’autres services internes de votre entreprise | Désactivez-les. |
Les désactiver n’est pas une mesure de sécurité. Chaque point de terminaison doit quand même vérifier ses entrées et ses accès. Un attaquant peut trouver les points de terminaison sans documentation. Cela ne fait que retirer une carte. Amira en fait un paramètre, pour que chaque environnement où l’API fonctionne puisse choisir :
show_docs = os.environ.get("SHOW_DOCS", "true").lower() == "true"
app = FastAPI( # ... title, version, description and openapi_tags as before docs_url="/docs" if show_docs else None, redoc_url="/redoc" if show_docs else None, openapi_url="/openapi.json" if show_docs else None,)None désactive une page. Avec SHOW_DOCS=false, les trois chemins renvoient 404 dans la structure d’erreur habituelle. L’API fonctionne comme avant.
Exercice guidé : protéger le contrat
Section intitulée « Exercice guidé : protéger le contrat »- Installez les outils de test et créez
ticket_api/export_contract.pyettests/test_contract.py. - Exécutez
python -m ticket_api.export_contract. Ouvrezcontract/openapi.jsonet trouvez le schémaClassificationet son exemple. - Exécutez
python -m pytest. Les deux tests réussissent. - Renommez
model_versionenversion, comme dans l’exemple détaillé. Lancez les tests. Le test du contrat échoue et montre le renommage. - Annulez le renommage. Relancez les tests. Les deux réussissent.
- Remplacez le sujet de l’exemple
TicketInpar"Hello"(bonjour), sans modifier l’exempleClassification. Lancez les tests. Quel test échoue maintenant, et pourquoi? Annulez ensuite le changement. - Ajoutez le paramètre
SHOW_DOCS. Démarrez le serveur avecSHOW_DOCS=false($env:SHOW_DOCS = "false"dans PowerShell). Vérifiez que/docsrenvoie404et que/healthfonctionne encore. Redémarrez-le ensuite sans la variable.
Afficher la réponse à l’étape 6
Les deux tests échouent. Le test du contrat échoue parce que l’exemple fait partie du document. Le document a donc changé. Le test de l’exemple échoue parce que "Hello" est classé dans other (autre), ce qui n’est pas la réponse documentée. La documentation montrerait une requête et une réponse qui ne vont pas ensemble.
À vous de jouer : un changement voulu
Section intitulée « À vous de jouer : un changement voulu »Le service de soutien demande un nouveau champ de réponse facultatif, explanation. Ce court texte explique pourquoi le classificateur a choisi la catégorie, par exemple "matched: charged, invoice" (correspondances : facturé, facture). Ajoutez-le pour modifier le contrat volontairement :
- Ajoutez le champ à
Classification, avec une description et une valeur dans l’exemple. Définissez sa valeur dans la route. (Le classificateur peut renvoyer les mots correspondants dansPrediction.) - Lancez les tests et lisez le message d’échec.
- Décidez : ce changement brise-t-il la compatibilité? Quelle nouvelle
versionl’API reçoit-elle? - Mettez à jour le contrat révisé, examinez les différences et relancez les tests.
Afficher la réponse à l’étape 3
Il ne brise pas la compatibilité : les anciens clients ne lisent pas explanation. Tous les champs qu’ils lisent sont encore présents. Le chemin reste donc /v1 et la version passe de 1.0.0 à 1.1.0. Si vous le rendiez plutôt obligatoire dans les requêtes, les anciens clients échoueraient. Ce changement briserait la compatibilité. Retirez le champ avant le prochain module et exportez de nouveau le contrat pour que votre projet corresponde au cours.
Erreur courante
Mettre à jour le contrat pour faire réussir le test
Symptôme : le test du contrat échoue. La correction la plus rapide consiste à exécuter export_contract, puis à relancer les tests. Ils réussissent. Une semaine plus tard, un client cesse de fonctionner.
Cause : le test montre seulement que le contrat a changé. L’exportation accepte tout changement sans qu’on le lise. La copie révisée n’est utile que si quelqu’un la révise.
Solution :
- Avant d’exporter, lisez les différences montrées par le test. Dites en une phrase ce qui a changé et pourquoi.
- Décidez si le changement brise la compatibilité. Si oui, il faut une nouvelle version du chemin ou un accord avec chaque client, pas seulement un nouveau fichier.
- Si vous utilisez Git, gardez la modification de
contract/openapi.jsondans le même commit que le code qui l’a causée. La personne qui révise verra ainsi les deux.
Vérifiez votre compréhension
Les tests réussissent sur votre ordinateur, mais échouent sur celui d'un collègue en montrant une différence dans le contrat. Aucun de vous n'a changé le code. Qu'est-ce qui pourrait être différent?
Afficher la réponse
Les paquets. Une version différente de FastAPI ou de Pydantic peut représenter les mêmes modèles légèrement différemment dans le document OpenAPI. Par exemple, elle peut ajouter un champ dans un schéma. C’est pourquoi le projet contient un fichier de verrouillage. Installez les paquets à partir de requirements-lock.txt pour que tout le monde ait les mêmes versions. Autre cause : SHOW_DOCS=false dans un terminal, ce qui retire /openapi.json.
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.
Vérifiez votre compréhension
Vérification du module : nommez les deux tests de cette leçon et, pour chacun, un changement qui le fait échouer.
Afficher la réponse
test_openapi_matches_the_reviewed_contract échoue pour toute modification du contrat : champ renommé, nouveau point de terminaison, limite différente, description ou exemple modifié. test_documented_example_gives_the_documented_result échoue quand la requête documentée ne donne plus la réponse documentée. Par exemple : une règle du classificateur modifiée, un nouveau champ obligatoire ou un exemple non valide. Ensemble, ils garantissent qu’une modification du contrat résulte d’une décision. Ils vérifient aussi que les exemples de /docs fonctionnent quand on les envoie sans modification.
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
- Conservez une copie révisée du document OpenAPI dans le projet et comparez l’API à cette copie dans un test.
- Comparez du texte mis en forme pour qu’un échec montre exactement quelles lignes ont changé.
- Testez les exemples documentés en les envoyant, pour que
/docsne montre jamais une requête qui échoue. - Quand le test du contrat échoue, lisez les différences et prenez une décision. N’exportez pas seulement pour faire réussir le test.
- Exposez la documentation en production seulement si les utilisateurs de l’API en ont besoin. La cacher ne constitue pas une protection.
Votre API se documente maintenant elle-même, et la documentation reste exacte. Dans le prochain module, vous utilisez le même document OpenAPI dans Postman.
Leçon suivante : Premières requêtes dans Postman