Retour au cours

backend / fastapi

Documentation OpenAPI avancée

Leçon 221 exercice

Explication

Ce que vous allez apprendre

  • Configurer les métadonnées globales de l'application (titre, contact, tags) pour Swagger
  • Enrichir un endpoint avec summary, description et des exemples de payload
  • Masquer un endpoint interne de la documentation publique avec include_in_schema=False
  • Comprendre le rôle du document OpenAPI sous-jacent à /openapi.json
  • Personnaliser la fonction de génération du schéma OpenAPI pour des besoins avancés

Dans quel contexte ?

L'équipe frontend qui consomme l'API app/main.py se plaint de devoir régulièrement demander sur Slack "à quoi ressemble le payload attendu pour créer une commande ?", faute d'exemples concrets dans la documentation Swagger générée automatiquement. Ajouter summary, description et un exemple de payload directement sur l'endpoint POST /orders élimine cet aller-retour : la documentation interactive sur /docs devient auto-suffisante pour intégrer l'API sans poser de question.

Se souvenir d'un point vu dès la leçon 1

La toute première leçon de ce cours soulignait déjà que la documentation Swagger de FastAPI est générée directement depuis le code. Elle ne peut donc jamais être "en retard" par rapport à l'implémentation réelle.

Cette leçon va plus loin : comment enrichir CETTE documentation pour qu'elle devienne vraiment utile ? Utile à une équipe frontend ou à des développeurs tiers, pas juste techniquement correcte.

Avant ça, il faut comprendre le standard qui se cache derrière Swagger : OpenAPI. Swagger UI et ReDoc ne sont que des INTERFACES visuelles pour lire un document sous-jacent standardisé.

Ce schéma OpenAPI, accessible à /openapi.json, décrit formellement chaque route, chaque paramètre, chaque schéma de donnée. Comprendre qu'il existe ce document central explique pourquoi il peut aussi être exploité par des outils tiers.

Par exemple pour générer automatiquement un client TypeScript côté frontend. Une fois ce document compris, voyons comment l'enrichir concrètement.

Ajouter une description, des examples, un summary à chaque endpoint change l'expérience de qui découvre l'API pour la première fois. Au lieu de deviner ce qu'attend un champ en lisant le code source, il le voit directement dans une interface interactive.

Une fois la documentation enrichie, il reste un besoin différent à couvrir : cacher ce qui ne doit pas être public. include_in_schema=False retire un endpoint de la documentation SANS le désactiver pour autant.

Utile pour des routes internes de debug qui existent mais ne doivent pas apparaître dans une documentation destinée à des consommateurs externes. Une distinction importante entre "exister" et "être visible".

Prérequis

Cette leçon suppose la leçon 1 déjà acquise (génération automatique de /docs) : elle explore comment enrichir cette base, pas comment l'activer.

Élément à enrichirParamètre FastAPI
Titre, contact, tags globauxFastAPI(title=..., contact=..., openapi_tags=...)
Résumé/description d'un endpointsummary=..., description=...
Exemple de payloadField(examples=[...]) ou openapi_examples
Masquer un endpoint interneinclude_in_schema=False

Pour aller plus loin, sache que FastAPI permet aussi de personnaliser le schéma généré. Pour des besoins avancés, comme documenter globalement un schéma d'authentification.

FastAPI permet de reprendre la main sur la fonction qui génère le schéma OpenAPI final. Pour l'enrichir manuellement au-delà de ce que la génération automatique produit seule.

Commandes & code

Documentation OpenAPI avancée

python
from fastapi import FastAPI

app = FastAPI(
    title="API E-commerce",
    description="API complète de gestion de produits, commandes et utilisateurs.",
    version="2.1.0",
    contact={"name": "Équipe Backend", "email": "backend@example.com"},
    license_info={"name": "MIT"},
    openapi_tags=[
        {"name": "products", "description": "Gestion du catalogue produits"},
        {"name": "orders", "description": "Cycle de vie des commandes"},
        {"name": "auth", "description": "Authentification et gestion de session"},
    ],
)
python
# Documenter un endpoint en détail — summary, description, exemples
from pydantic import BaseModel, Field


class ProductCreate(BaseModel):
    name: str = Field(..., description="Nom commercial du produit", examples=["Clavier mécanique RGB"])
    price: float = Field(..., gt=0, description="Prix en euros TTC", examples=[89.99])


@app.post(
    "/products",
    summary="Créer un nouveau produit",
    description="""
    Crée un produit dans le catalogue. Nécessite le rôle 'editor' ou supérieur.

    Le prix doit être strictement positif. Le stock initial est à 0 par défaut,
    à ajuster via l'endpoint /products/{id}/stock.
    """,
    response_description="Le produit créé, avec son ID généré",
    tags=["products"],
)
def create_product(data: ProductCreate):
    return data
python
# Exemples multiples dans le schéma OpenAPI (via Config / json_schema_extra)
class OrderCreate(BaseModel):
    product_id: int
    quantity: int

    model_config = {
        "json_schema_extra": {
            "examples": [
                {"product_id": 1, "quantity": 2},
                {"product_id": 5, "quantity": 1},
            ]
        }
    }
python
# Masquer un endpoint interne de la documentation publique
@app.get("/internal/debug", include_in_schema=False)
def debug_endpoint():
    return {"debug": True}


# Regrouper et personnaliser via un router dédié à l'admin, hors doc publique
admin_router = APIRouter(prefix="/admin", include_in_schema=False)
python
# Personnaliser le schéma OpenAPI généré — ex. ajouter la sécurité JWT globalement
from fastapi.openapi.utils import get_openapi


def custom_openapi():
    if app.openapi_schema:
        return app.openapi_schema

    openapi_schema = get_openapi(
        title=app.title,
        version=app.version,
        description=app.description,
        routes=app.routes,
    )

    openapi_schema["components"]["securitySchemes"] = {
        "BearerAuth": {"type": "http", "scheme": "bearer", "bearerFormat": "JWT"}
    }
    for path in openapi_schema["paths"].values():
        for operation in path.values():
            operation["security"] = [{"BearerAuth": []}]

    app.openapi_schema = openapi_schema
    return app.openapi_schema


app.openapi = custom_openapi
python
# Générer le client TypeScript/Python à partir du schéma OpenAPI (workflow courant en équipe)
# openapi-typescript-codegen, openapi-python-client, ou orval côté frontend
# npx openapi-typescript http://localhost:8000/openapi.json -o ./frontend/src/api/schema.ts

Résumé

  • title, description, openapi_tags structurent une documentation Swagger UI claire par domaine métier.
  • Field(description=..., examples=[...]) enrichit le schéma OpenAPI directement depuis les modèles Pydantic.
  • include_in_schema=False masque des endpoints internes (debug, admin technique) de la doc publique.
  • Le schéma OpenAPI généré peut être exporté pour générer automatiquement des clients typés côté frontend.

Exercices pratiques

1 disponible
1

Mission : le client TypeScript généré qui ignore les routes admin

Objectif : Comprendre l'impact d'include_in_schema=False sur la génération automatique de client, et enrichir la documentation d'un endpoint consommé par une équipe externe.

Contexte

L'équipe frontend exécute npx openapi-typescript http://localhost:8000/openapi.json pour générer un client typé, comme vu dans cette leçon. Le client généré ne contient aucune des routes de admin_router, alors que ces routes répondent parfaitement quand elles sont appelées manuellement avec le bon token. Par ailleurs, l'équipe continue de demander sur Slack le format exact du payload attendu par POST /orders, qui n'a encore ni summary, ni description, ni exemple.

Résoudre l’exercice →