Python Framework

FastAPI

FastAPI est le framework Python asynchrone basé sur les type hints. Pydantic valide vos données, Starlette gère le HTTP, et la documentation OpenAPI s'écrit toute seule.

intermediate15 min readUpdated 16 sept. 2026
main.py
python
# main.py
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    price: float


@app.post("/items", status_code=201)
async def create_item(item: Item) -> Item:
    return item
Sortie
2018
Langage
Python
Basé sur
Starlette + Pydantic
Idée centrale
Les type hints comme contrat
Docs
OpenAPI et Swagger UI
Version actuelle
0.11x

Pourquoi c'est important

Pourquoi FastAPI semble magique (alors qu'il ne l'est pas)

Les types sont l'API

Déclarez un paramètre ou un modèle avec des type hints et FastAPI valide les requêtes, sérialise les réponses et documente le point de terminaison à partir de cette même déclaration.

L'async quand vous en avez besoin

Construit sur la boîte à outils ASGI de Starlette, les points de terminaison async gèrent les E/S concurrentes sans bloquer la boucle d'événements.

La doc gratuite

Chaque point de terminaison apparaît dans Swagger UI et ReDoc, générés à partir du schéma OpenAPI que FastAPI construit au démarrage.

Le tableau complet

Types, Pydantic, dépendances

Les type hints décrivent le contrat, Pydantic l'impose, et les dépendances injectent les éléments partagés dont une requête a besoin.

Type hints

Décrire

L'annotation des paramètres indique à FastAPI d'où vient une valeur et quelle forme elle a, ainsi la validation s'effectue avant l'exécution de votre fonction.

Modèles Pydantic

Valider

Les classes BaseModel analysent, contraignent et rejettent les données, tout en servant de contrat de réponse et de schéma généré.

Dépendances

Injecter

Depends() compose la logique partagée telle que les sessions, l'authentification et les paramètres, résolvant le graphe par requête et réutilisant les résultats mis en cache.

HTML5 en un coup d'oeil

La boîte à outils FastAPI

Opérations de chemin

@app.get et @app.post lient une URL et une fonction, avec des paramètres de chemin et de requête typés.

Modèles Pydantic

Les classes BaseModel valident les corps de requête et structurent les réponses.

Documentation automatique

Swagger UI sur /docs et ReDoc sur /redoc, générés directement depuis votre code.

Dépendances

Depends() injecte les sessions, les utilisateurs, la pagination et la configuration.

Support Async

Les points de terminaison async def s'exécutent sur ASGI et attendent les E/S de manière concurrente.

Tâches de fond

Exécutez du travail après l'envoi de la réponse sans avoir besoin d'ajouter une file d'attente.

Le guide complet

FastAPI: Tout ce que vous devez savoir

Qu’est-ce que FastAPI ?

FastAPI est un framework web Python moderne pour la création d’API. Son concept fondamental est que vos type hints font office de contrat. Déclarez un paramètre comme int, un corps de requête comme un modèle Pydantic, ou un type de retour comme ItemOut, et le framework valide la requête, sérialise la réponse et rédige la documentation — tout cela à partir de cette unique déclaration.

Il repose sur deux bibliothèques dont vous devez retenir le nom. Starlette fournit la boîte à outils ASGI : le routage, le middleware et le cycle requête/réponse. Pydantic s’occupe de la validation et de la sérialisation. FastAPI est la couche qui les combine, ajoute l’injection de dépendances et génère un schéma OpenAPI au démarrage.

Le résultat est qu’une fonction simple et lisible vous offre un endpoint validé avec une documentation interactive. La magie est bien réelle, mais elle est mécanique, et c’est précisément l’objet de ce guide : comprendre ces mécanismes.

Opérations de chemin

Une opération de chemin (path operation) est une fonction liée à une méthode HTTP et à une URL. Le décorateur nomme la méthode, et la signature de la fonction indique à FastAPI ce qu’il doit attendre.

# main.py
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello, world"}


@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
    return {"item_id": item_id, "q": q}

Le segment {item_id} est un paramètre de chemin, et son annotation int signifie que FastAPI convertit la chaîne de caractères et renvoie automatiquement une erreur 422 s’il n’y parvient pas. Tout élément qui ne figure pas dans le chemin est traité comme un paramètre de requête, et une valeur par défaut de None le rend optionnel. Il n’y a aucun parsing manuel ni aucun request.args.

Groupez les opérations liées à l’aide d’un APIRouter, qui peut comporter un préfixe et des tags pour la documentation.

from fastapi import APIRouter

router = APIRouter(prefix="/items", tags=["items"])


@router.get("")
async def list_items():
    return []


@router.post("", status_code=201)
async def create_item():
    return {}

Incluez ensuite le router dans l’application : app.include_router(router). C’est ainsi qu’un projet FastAPI reste modulaire.

Modèles Pydantic

Un modèle Pydantic est une classe qui hérite de BaseModel. Il analyse l’entrée, convertit les types compatibles, applique des contraintes et lève une erreur de validation claire lorsqu’un problème survient.

# app/schemas.py
from datetime import datetime

from pydantic import BaseModel, Field


class ItemBase(BaseModel):
    name: str = Field(min_length=1, max_length=120)
    price: float = Field(gt=0)
    tags: list[str] = []


class ItemCreate(ItemBase):
    pass


class ItemOut(ItemBase):
    id: int
    created_at: datetime

    model_config = {"from_attributes": True}

Field ajoute des contraintes et des métadonnées. model_config = {"from_attributes": True} permet à Pydantic de lire des attributs depuis un objet ORM, vous permettant ainsi de retourner directement une ligne de base de données et de la voir sérialisée. La séparation entre ItemCreate et ItemOut est délibérée : le modèle d’entrée n’a pas de id, et le modèle de sortie peut exposer des champs que le client ne peut pas définir.

Utilisez un modèle Pydantic comme type pour le corps de la requête et FastAPI l’analysera, le validera et vous remettra un objet typé.

@app.post("/items", response_model=ItemOut, status_code=201)
async def create_item(item: ItemCreate):
    return await repo.create(item)

Si le corps est invalide, FastAPI retourne une erreur 422 avec la liste exacte des champs en échec. Vous n’avez pas eu à écrire une seule ligne de validation.

Corps de requête et validation

FastAPI distingue les sources de données par leur position et leur type :

  • Les paramètres de chemin (path parameters) proviennent de l’URL.
  • Les paramètres de requête (query parameters) correspondent à tout le reste de la signature.
  • Un modèle Pydantic représente le corps de la requête (request body).
  • Header, Cookie, Form et File marquent ces entrées spéciales.
from fastapi import Header, Query


@app.get("/search")
async def search(
    q: str = Query(min_length=1, max_length=50),
    limit: int = Query(default=20, ge=1, le=100),
    user_agent: str | None = Header(default=None),
):
    return {"q": q, "limit": limit, "user_agent": user_agent}

Les contraintes sur les paramètres de requête sont appliquées de la même manière que sur les modèles, et la documentation générée affiche ces limites. Cette cohérence est la raison pour laquelle le code FastAPI est si compact.

Documentation automatique

Comme le schéma est généré à partir du code, la documentation n’est jamais obsolète. Lancez l’application et ouvrez /docs pour Swagger UI, où vous pouvez appeler les endpoints directement, ou /redoc pour une référence plus épurée.

app = FastAPI(
    title="Inventory API",
    version="1.0.0",
    description="Items, stock levels and orders.",
)

Vous pouvez ajouter des résumés, des descriptions et des exemples aux opérations de chemin et aux champs, et ils apparaîtront dans la documentation. Pour les équipes frontend, ce même schéma peut générer des clients typés, permettant ainsi à l’API et à ses consommateurs de rester synchronisés. C’est tout l’avantage de traiter les types comme un contrat.

Dépendances

Une dépendance est une fonction que FastAPI appelle avant votre endpoint et dont la valeur de retour est injectée. Depends() est le mécanisme, et il est composable.

from fastapi import Depends, HTTPException


async def get_session() -> AsyncSession:
    async with SessionLocal() as session:
        yield session


async def get_current_user(
    token: str = Depends(get_token),
    session: AsyncSession = Depends(get_session),
) -> User:
    user = await authenticate(session, token)
    if user is None:
        raise HTTPException(status_code=401, detail="Not authenticated")
    return user
@app.get("/me", response_model=UserOut)
async def read_me(user: User = Depends(get_current_user)):
    return user

Les dépendances peuvent être synchrones ou asynchrones, peuvent utiliser yield pour exécuter un nettoyage après la réponse, et sont mises en cache au sein d’une requête, sehingga get_session n’est appelé qu’une seule fois même si plusieurs dépendances en ont besoin. Elles constituent l’emplacement idiomatique pour les sessions de base de données, l’authentification, la pagination et les feature flags.

Async versus sync

FastAPI supporte les endpoints def et async def, et ce choix est important.

  • Un endpoint async def s’exécute sur la boucle d’événements (event loop). Chaque appel d’E/S à l’intérieur doit être attendu via une bibliothèque async, sinon il bloque la boucle pour toutes les autres requêtes.
  • Un endpoint def classique est exécuté dans un pool de threads ; les appels bloquants sont donc sans danger, mais chaque requête occupe un thread.
# Async: use an async driver and await it
@app.get("/items")
async def list_items(session: AsyncSession = Depends(get_session)):
    result = await session.execute(select(Item))
    return result.scalars().all()


# Sync: blocking code is fine; FastAPI offloads it
@app.get("/report")
def build_report():
    return generate_report()  # CPU or blocking I/O

La règle est simple : tout en async, ou tout en sync. Insérer un appel bloquant dans un endpoint async def est le bug de performance le plus courant avec FastAPI, et il reste invisible jusqu’à l’arrivée du trafic.

Erreurs et codes de statut

Levez une exception HTTPException pour retourner une erreur avec un code de statut et un détail au format JSON. FastAPI la transforme en réponse et documente le code de statut.

from fastapi import HTTPException, status


@app.get("/items/{item_id}", response_model=ItemOut)
async def read_item(item_id: int):
    item = await repo.get(item_id)
    if item is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Item not found",
        )
    return item

Pour garantir une structure d’erreur cohérente sur l’ensemble de l’API, enregistrez des gestionnaires d’exceptions (exception handlers) qui convertissent les erreurs du framework et du domaine en une enveloppe unique. Gardez le champ detail stable ; les clients s’appuieront dessus pour le parsing.

Tâches d’arrière-plan

BackgroundTasks exécute des tâches après l’envoi de la réponse, au sein du même processus. C’est l’option légère pour envoyer un e-mail ou écrire une ligne de journal d’audit.

from fastapi import BackgroundTasks


def send_receipt(email: str) -> None:
    ...


@app.post("/orders", status_code=201)
async def create_order(
    order: OrderCreate,
    tasks: BackgroundTasks,
):
    saved = await repo.create(order)
    tasks.add_task(send_receipt, saved.email)
    return saved

Les tâches d’arrière-plan ne sont pas une file d’attente de jobs (job queue). Elles s’exécutent dans le processus, elles sont donc perdues si le worker redémarre et elles entrent en concurrence avec la gestion des requêtes. Pour tout besoin de persistance, déléguez plutôt à Celery, RQ ou à une file d’attente managée.

Bases de données

FastAPI ne possède pas de couche de base de données, vous devez donc choisir la vôtre. Les choix les plus courants sont SQLAlchemy avec un moteur asynchrone ou SQLModel, qui combine SQLAlchemy et Pydantic. Le pattern de dépendance permet de fournir la session et de la fermer après la requête.

# app/db.py
from collections.abc import AsyncIterator

from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

engine = create_async_engine(settings.database_url, pool_pre_ping=True)
SessionLocal = async_sessionmaker(engine, expire_on_commit=False)


async def get_session() -> AsyncIterator[AsyncSession]:
    async with SessionLocal() as session:
        yield session
@router.post("", response_model=ItemOut, status_code=201)
async def create_item(
    payload: ItemCreate,
    session: AsyncSession = Depends(get_session),
):
    item = Item(**payload.model_dump())
    session.add(item)
    await session.commit()
    await session.refresh(item)
    return item

Utilisez expire_on_commit=False pour que les objets restent utilisables après le commit, et gardez les transactions à l’intérieur de la requête. Si vous préférez un driver synchrone, utilisez des endpoints def classiques afin que FastAPI puisse les déléguer au pool de threads.

Sécurité

FastAPI fournit les briques de base pour OAuth2 et JWT sans imposer de système d’authentification complet. OAuth2PasswordBearer extrait le jeton, et python-jose ou pyjwt le vérifie.

from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


@app.get("/users/me", response_model=UserOut)
async def read_me(
    token: str = Depends(oauth2_scheme),
    session: AsyncSession = Depends(get_session),
):
    user = await get_user_from_token(session, token)
    if user is None:
        raise HTTPException(status_code=401, detail="Invalid token")
    return user

Stockez les hachages de mots de passe avec passlib ou argon2-cffi, jamais en texte brut. Effectuez le hachage et la vérification en dehors du point de terminaison, conservez vos secrets dans les paramètres, et configurez avec soin la limitation de débit (rate limiting) et le CORS. FastAPI vous donne les outils ; c’est à vous de définir la politique de sécurité.

Configuration et paramètres

Regroupez votre configuration dans un seul objet typé au lieu de lire les variables d’environnement partout dans le code. pydantic-settings valide l’environnement au démarrage et vous offre la même sécurité de typage que vos modèles.

# app/config.py
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", env_prefix="APP_")

    database_url: str
    secret_key: str
    access_token_minutes: int = 30


settings = Settings()
from functools import lru_cache

from app.config import Settings


@lru_cache
def get_settings() -> Settings:
    return Settings()
@app.get("/info")
async def info(settings: Settings = Depends(get_settings)):
    return {"token_minutes": settings.access_token_minutes}

Comme les paramètres sont injectés comme n’importe quelle autre dépendance, les tests peuvent les surcharger avec app.dependency_overrides[get_settings]. L’absence de variables obligatoires provoque un échec au démarrage plutôt qu’à la première requête, ce qui est précisément le moment où vous voulez le savoir.

Tests

TestClient enveloppe l’application dans un client de type WSGI et effectue de véritables requêtes au sein du processus, incluant la validation et le remplacement des dépendances.

# tests/test_items.py
from fastapi.testclient import TestClient

from app.main import app

client = TestClient(app)


def test_create_item():
    response = client.post("/items", json={"name": "Cup", "price": 9.5})
    assert response.status_code == 201
    assert response.json()["name"] == "Cup"


def test_invalid_price():
    response = client.post("/items", json={"name": "Cup", "price": -1})
    assert response.status_code == 422

Utilisez app.dependency_overrides pour remplacer la session de base de données par une session de test, et rédigez des tests asynchrones avec httpx.AsyncClient lorsque vous devez tester directement des endpoints asynchrones. Comme les dépendances sont explicites, les remplacer dans les tests est très simple.

Bonnes pratiques

  • Déclarez explicitement les modèles de requête et de réponse ; n’acceptez et ne retournez jamais de dictionnaires bruts.
  • Gardez vos endpoints async def entièrement async, ou utilisez def pour que FastAPI délègue les tâches bloquantes.
  • Placez la logique partagée dans des dépendances Depends() plutôt que de la répéter pour chaque route.
  • Segmentez l’application avec APIRouter et attribuez à chaque router un préfixe et des tags.
  • Ajoutez des contraintes avec Field et Query plutôt que de valider manuellement.
  • Levez des HTTPException avec le code de statut approprié et un detail stable.
  • Surchargez les dépendances dans vos tests au lieu de patcher les composants internes.
  • Conservez vos secrets dans des objets de configuration lus depuis l’environnement.

Erreurs courantes

  • Appeler du code bloquant à l’intérieur d’un endpoint async def, ce qui bloque l’event loop.
  • Retourner des objets ORM sans response_model, exposant ainsi des colonnes internes.
  • Oublier from_attributes = True et se demander pourquoi la sérialisation échoue.
  • Écrire un seul main.py géant au lieu d’utiliser des routers et des modules.
  • Utiliser BackgroundTasks comme une file d’attente de tâches (job queue) durable.
  • Partager une seule session entre plusieurs requêtes ou tâches asynchrones.
  • Valider deux fois la même entrée parce que le modèle Pydantic a été ignoré.
  • Capturer toutes les exceptions et retourner un code 200 avec un corps d’erreur.

Et après ?

FastAPI transforme les indices de type (type hints) en une API validée et documentée, c’est pourquoi il est devenu le choix par défaut pour les nouveaux services Python. Pour comparer les compromis, consultez le guide Flask pour l’approche synchrone minimale et celui de Django pour un framework complet incluant un ORM et une interface d’administration. Approfondissez ensuite la notion de contrat d’API avec OpenAPI et les règles de conception des [REST APIs](/guides/rest], et assurez-vous que vos endpoints méritent d’être documentés en définissant correctement votre modèle de ressources.

En pratique

Un point de terminaison, quatre couches

Le modèle déclare la forme, l'opération de chemin l'expose, les dépendances fournissent le contexte, et l'appel à la base de données effectue le travail.

app/main.py
from fastapi import FastAPI, HTTPException

from app.schemas import ItemOut

app = FastAPI(title="Inventory API")


@app.get("/items/{item_id}", response_model=ItemOut)
async def read_item(item_id: int):
    item = await db.items.get(item_id)
    if item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    return item

E/S Async et bloquantes

Un point de terminaison async s'exécute sur la boucle d'événements ; un appel bloquant fige donc toutes les autres requêtes. Utilisez un driver async avec async def, ou un point de terminaison def classique que FastAPI exécutera dans un pool de threads.

Préférer
@app.get("/items")
async def list_items(session: AsyncSession = Depends(get_session)):
    result = await session.execute(select(Item))
    return result.scalars().all()
Éviter
@app.get("/items")
async def list_items():
    # blocks the event loop for the whole query
    return session.query(Item).all()

Structurer la réponse

Un response_model filtre la sortie pour ne garder que les champs déclarés et les documente, évitant ainsi que des colonnes internes ne fuitent vers les clients.

Préférer
@app.get("/users/{user_id}", response_model=UserOut)
async def get_user(user_id: int):
    return await repo.get(user_id)
Éviter
@app.get("/users/{user_id}")
async def get_user(user_id: int):
    # returns password_hash and every other column
    return await repo.get(user_id)

Compromis

FastAPI est-il le choix par défaut idéal ?

FastAPI optimise les API typées et le débit asynchrone. C'est excellent pour les services, mais moins adapté quand l'application consiste principalement en des pages rendues côté serveur.

Strengths

  • Moins de code, moins de bugs

    La validation, la sérialisation et la documentation proviennent toutes des mêmes type hints, il n'y a donc pas de second schéma à synchroniser.

  • Documentation automatique

    Un schéma OpenAPI vivant et une doc interactive rendent l'API explorable pour les équipes frontend et clients dès le premier jour.

  • Conçu pour le Python moderne

    Les points de terminaison async, l'injection de dépendances et Pydantic v2 le rendent rapide et agréable sans être un framework lourd.

Trade-offs

  • L'async a ses pièges

    Un seul appel bloquant à l'intérieur d'un point de terminaison async peut paralyser tout le worker. Vous devez savoir quelles bibliothèques sont réellement async.

  • C'est un framework d'API

    FastAPI ne fournit pas de templates, d'interface d'administration ou d'ORM. Les applications rendues côté serveur nécessitent une autre couche ou un framework différent.

  • Un écosystème plus jeune

    Les versions de Pydantic et SQLAlchemy évoluent rapidement et les patterns changent. Attendez-vous à devoir maintenir vos dépendances à jour.

FAQ

Foire aux questions

Keep learning

Related topics from the roadmap.

$ commencer à apprendre

Prêt à apprendre FastAPI ?

Notre tutoriel interactif vous guide à travers FastAPI pas à pas — avec des quiz et du vrai code que vous pouvez exécuter dans le navigateur.