Lecture : 11 minutes · Niveau : intermédiaire · Mise à jour : avril 2026
FastAPI est devenu le framework Python de référence pour créer des API HTTP modernes. Performance proche de Node.js, syntaxe basée sur les type hints, validation automatique, documentation OpenAPI générée gratuitement : il offre un excellent compromis productivité-qualité. Pour une PME qui doit exposer une fonctionnalité interne via HTTP (intégration avec un autre outil, dashboard, mobile), FastAPI permet de livrer en quelques heures ce qui en demanderait des jours en frameworks plus lourds.
Voir aussi → Python pour PME : guide pratique.
Sommaire
- Pourquoi FastAPI plutôt que Flask ou Django
- Premier endpoint en 5 minutes
- Validation avec Pydantic
- Base de données avec SQLAlchemy
- Authentification simple
- Gestion des erreurs propre
- Tests automatisés
- Déploiement en production
- FAQ
1. Pourquoi FastAPI plutôt que Flask ou Django
Les trois frameworks Python web dominants ont des positionnements différents :
- Flask : minimaliste, flexible, excellent pour des projets très simples. Ne fait rien sans bibliothèques tierces (extensions). Mature, énorme écosystème.
- Django : framework complet (ORM, admin, auth, templates, migrations). Idéal pour des applications web full-stack avec rendu serveur. Lourd pour une simple API.
- FastAPI : moderne, async natif, validation automatique via type hints, documentation OpenAPI générée. Performant. Devenu la référence pour les API JSON.
Pour une PME qui veut juste exposer quelques endpoints HTTP : FastAPI est le choix le plus productif en 2026. Pour un site web full-stack avec back-office riche : Django reste pertinent. Flask reste une option pour ceux qui veulent une base ultra-minimaliste.
2. Premier endpoint en 5 minutes
# Installation
pip install fastapi uvicorn[standard]
main.py :
from fastapi import FastAPI
app = FastAPI(title="API ma-pme", version="0.1.0")
@app.get("/")
def racine():
return {"message": "API opérationnelle"}
@app.get("/sante")
def sante():
return {"status": "ok"}
@app.get("/clients/{client_id}")
def get_client(client_id: int):
return {"id": client_id, "nom": f"Client {client_id}"}
Lancement :
uvicorn main:app --reload --host 0.0.0.0 --port 8000
Tester :
curl http://localhost:8000/
curl http://localhost:8000/clients/42
Documentation interactive auto-générée : http://localhost:8000/docs (Swagger UI) et http://localhost:8000/redoc (ReDoc).
--reload redémarre le serveur à chaque changement de fichier — pratique en développement.
3. Validation avec Pydantic
FastAPI utilise Pydantic pour valider automatiquement les données entrantes et formater les sorties.
from pydantic import BaseModel, Field, EmailStr
from typing import Annotated
from datetime import datetime
class ClientCreate(BaseModel):
nom: str = Field(min_length=2, max_length=100)
email: EmailStr
telephone: str | None = None
actif: bool = True
class Client(ClientCreate):
id: int
cree_le: datetime
@app.post("/clients", response_model=Client, status_code=201)
def creer_client(data: ClientCreate):
# data est garanti valide ici (sinon FastAPI a déjà renvoyé 422)
nouveau = {
"id": 1,
"cree_le": datetime.now(),
**data.model_dump(),
}
return nouveau
FastAPI valide automatiquement :
– Les types : un int reçu comme string lèvera une erreur 422
– Les contraintes : min_length, max_length, regex, etc.
– Les types complexes : email valide, dates au format ISO, énumérations
– Les champs obligatoires vs optionnels
L’utilisateur reçoit une réponse 422 détaillée explicitement quels champs sont invalides — sans une ligne de code de validation côté serveur.
Query params et headers
from fastapi import Query, Header
@app.get("/clients")
def lister_clients(
skip: int = 0,
limit: int = Query(10, le=100),
actifs_seulement: bool = False,
auth: str = Header(...),
):
return {"skip": skip, "limit": limit, "actifs_seulement": actifs_seulement}
Query(le=100) : limite max 100. Header(...) : header obligatoire.
4. Base de données avec SQLAlchemy
Pour persister des données, SQLAlchemy 2.x est le standard. Approche moderne avec async :
from sqlalchemy import String
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
DATABASE_URL = "postgresql+asyncpg://user:pass@localhost/db"
engine = create_async_engine(DATABASE_URL)
async_session = async_sessionmaker(engine, expire_on_commit=False)
class Base(DeclarativeBase):
pass
class ClientDB(Base):
__tablename__ = "clients"
id: Mapped[int] = mapped_column(primary_key=True)
nom: Mapped[str] = mapped_column(String(100))
email: Mapped[str] = mapped_column(String(200), unique=True)
Dépendance pour injection de session :
from fastapi import Depends
async def get_db():
async with async_session() as session:
yield session
@app.get("/clients")
async def lister_clients(db: AsyncSession = Depends(get_db)):
from sqlalchemy import select
result = await db.execute(select(ClientDB))
return result.scalars().all()
Pour des projets simples, SQLite suffit (sqlite+aiosqlite:///app.db). Pour la production sérieuse : PostgreSQL.
Migrations avec Alembic
alembic init migrations génère un dossier de migrations. Chaque changement de schéma : alembic revision --autogenerate -m "ajout colonne x" puis alembic upgrade head. Standard incontournable pour gérer l’évolution du schéma de production.
5. Authentification simple
API key dans le header
Approche pragmatique pour API interne ou client connu :
from fastapi import Security, HTTPException, status
from fastapi.security import APIKeyHeader
api_key_header = APIKeyHeader(name="X-API-Key")
API_KEYS = {"abc123": "client-1", "xyz789": "client-2"}
def verifier_cle(key: str = Security(api_key_header)):
if key not in API_KEYS:
raise HTTPException(status_code=401, detail="Clé invalide")
return API_KEYS[key]
@app.get("/data", dependencies=[Depends(verifier_cle)])
def get_data():
return {"data": "secret"}
JWT pour utilisateurs
Pour des utilisateurs humains avec login, le standard est JWT (JSON Web Tokens). FastAPI a une intégration native avec OAuth2PasswordBearer. Implémenter cela en quelques dizaines de lignes — la documentation officielle FastAPI fournit un exemple complet.
OAuth pour intégration tierce
Pour authentifier des utilisateurs via Google, Microsoft, GitHub : authlib (authlib.org) propose des intégrations propres pour FastAPI.
6. Gestion des erreurs propre
from fastapi import HTTPException
@app.get("/clients/{client_id}")
async def get_client(client_id: int, db: AsyncSession = Depends(get_db)):
client = await db.get(ClientDB, client_id)
if not client:
raise HTTPException(
status_code=404,
detail=f"Client {client_id} non trouvé",
)
return client
Handler global pour exceptions custom
class BusinessError(Exception):
def __init__(self, message: str, code: str):
self.message = message
self.code = code
@app.exception_handler(BusinessError)
async def business_error_handler(request, exc: BusinessError):
return JSONResponse(
status_code=400,
content={"erreur": exc.code, "message": exc.message},
)
Cela évite de répéter try/except partout et standardise la forme des réponses d’erreur.
7. Tests automatisés
# test_main.py
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_sante():
response = client.get("/sante")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
def test_creer_client():
payload = {"nom": "Acme", "email": "contact@acme.test"}
response = client.post("/clients", json=payload)
assert response.status_code == 201
data = response.json()
assert data["nom"] == "Acme"
assert "id" in data
def test_creer_client_email_invalide():
payload = {"nom": "Acme", "email": "pas-un-email"}
response = client.post("/clients", json=payload)
assert response.status_code == 422
Lancement : pytest.
TestClient lance l’app en mémoire, sans serveur réel — rapide et fiable. Pour des tests d’intégration avec base de données, utiliser une base SQLite en mémoire ou une base de test dédiée.
8. Déploiement en production
Avec uvicorn + Nginx
# uvicorn directement (pas en dev mode)
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
Nginx en reverse proxy par devant gère HTTPS et la terminaison TLS.
Avec gunicorn + uvicorn workers
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
Plus robuste pour la production : gunicorn supervise les workers et les redémarre en cas de crash.
Avec Docker
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["gunicorn", "main:app", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000"]
Voir Docker en production pour PME pour les bonnes pratiques d’images.
Service systemd
/etc/systemd/system/api.service :
[Unit]
Description=API FastAPI
After=network.target
[Service]
Type=simple
User=apiuser
WorkingDirectory=/opt/api
Environment=DATABASE_URL=postgresql+asyncpg://...
ExecStart=/opt/api/.venv/bin/gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 127.0.0.1:8000
Restart=on-failure
[Install]
WantedBy=multi-user.target
Détails systemd dans systemd services tutoriel.
9. FAQ
FastAPI est-il adapté à un trafic important ?
Oui. Les benchmarks montrent FastAPI parmi les frameworks Python les plus rapides, comparable à Node.js Express. Avec uvicorn et plusieurs workers, on tient facilement quelques milliers de requêtes par seconde sur un serveur correct. Pour des charges extrêmes : envisager Go ou Rust, mais peu de PME en sont là.
Faut-il utiliser async partout ?
Pas obligatoire. FastAPI accepte des routes sync et async. Avantage du async : meilleure utilisation des cœurs CPU sur des opérations I/O-bound (DB, HTTP externe). Inconvénient : courbe d’apprentissage. Pour une API simple sans contention I/O, le sync va très bien et est plus facile à déboguer.
Comment gérer les fichiers uploadés ?
from fastapi import UploadFile, File
@app.post("/upload")
async def upload(file: UploadFile = File(...)):
contenu = await file.read()
# Sauvegarder ou traiter
return {"filename": file.filename, "size": len(contenu)}
Pour de gros fichiers : streamer plutôt que read() complet en mémoire.
Pydantic v1 ou v2 ?
v2 en 2026. Plus rapide (réécrit en Rust), API légèrement différente de v1. FastAPI 0.100+ est compatible v2. Migrer un projet v1 vers v2 demande quelques ajustements mais pas de refonte.
Comment paginer une route qui retourne une liste ?
Pattern standard avec skip et limit, ou avec un curseur pour des datasets volumineux. La bibliothèque fastapi-pagination (uriyyo.github.io/fastapi-pagination) factorise ce pattern.
Mon API a besoin de tâches en arrière-plan, comment ?
Trois niveaux : BackgroundTasks de FastAPI pour des actions courtes (envoyer un email après une réponse), Celery ou ARQ pour des jobs plus lourds avec une file Redis, ou un service séparé qui consomme une queue. Choisir selon la complexité réelle des besoins.
Articles liés (cluster Python pour PME)
- 👉 Python pour PME : guide pratique data, scripting, automatisation (pillar)
- 👉 Python pandas : traiter Excel et CSV en pratique
- 👉 Python pour scripting admin système
Article mis à jour le 25 avril 2026. Pour signaler une erreur ou suggérer une amélioration, écrivez-nous.