Construire un projet FastAPI
FastAPI est un framework web Python moderne et performant, basé sur les annotations de type Python et Pydantic. Il génère automatiquement la documentation OpenAPI.
Initialisation d'un projet
Utiliser uv pour gérer le projet Python et ses dépendances :
uv init my-api && cd my-api
uv add "fastapi[standard]"Cela génère un fichier pyproject.toml avec les métadonnées du projet et un fichier uv.lock verrouillant les dépendances.
TIP
Pour lancer le serveur de développement :
uv run fastapi devStructure du projet
├── app
│ ├── __init__.py
│ ├── main.py # Point d'entrée FastAPI
│ ├── config.py # Configuration (variables d'env)
│ ├── models
│ │ ├── __init__.py
│ │ └── cat.py # Modèles SQLAlchemy / Tortoise
│ ├── schemas
│ │ ├── __init__.py
│ │ └── cat.py # Schémas Pydantic
│ ├── routers
│ │ ├── __init__.py
│ │ └── cats.py # Routes /cats
│ ├── services
│ │ ├── __init__.py
│ │ └── cats.py # Logique métier
│ └── utils
│ ├── __init__.py
│ └── logger.py
├── tests
│ ├── __init__.py
│ └── test_cats.py
├── pyproject.toml
├── uv.lock
└── DockerfileConventions
Validation avec Pydantic
La validation des données entrantes et sortantes se fait avec des modèles Pydantic :
from pydantic import BaseModel, Field
class CatBase(BaseModel):
name: str = Field(min_length=1, description="Nom du chat")
age: int = Field(ge=0, description="Âge du chat")
class CatCreate(CatBase):
pass
class CatResponse(CatBase):
id: int
model_config = {"from_attributes": True}Routes (Routers)
Utiliser les APIRouter pour organiser les routes par domaine :
from fastapi import APIRouter, HTTPException, status
from app.schemas.cat import CatCreate, CatResponse
from app.services.cats import CatsService
router = APIRouter(prefix="/cats", tags=["cats"])
@router.get("/", response_model=list[CatResponse])
async def get_cats():
return await CatsService.find_all()
@router.get("/{cat_id}", response_model=CatResponse)
async def get_cat(cat_id: int):
cat = await CatsService.find_by_id(cat_id)
if not cat:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Cat non trouvé",
)
return cat
@router.post("/", response_model=CatResponse, status_code=status.HTTP_201_CREATED)
async def create_cat(cat: CatCreate):
return await CatsService.create(cat)Enregistrer les routers dans main.py :
from fastapi import FastAPI
from app.routers import cats
app = FastAPI(title="Mon API", version="1.0.0")
app.include_router(cats.router)Endpoints asynchrones
Privilégier les fonctions async pour les endpoints qui font des I/O (base de données, appels HTTP, etc.) :
# ✅ Bien : async pour les opérations I/O
@router.get("/cats")
async def get_cats():
return await db.fetch_all(query)
# ✅ Bien : sync pour les opérations CPU-bound
@router.get("/compute")
def compute_something():
return heavy_computation()Logging
Utiliser le module logging standard de Python avec une configuration structurée et le pattern lifespan (recommandé depuis FastAPI 0.93+, @app.on_event("startup") est déprécié) :
import logging
from contextlib import asynccontextmanager
from fastapi import FastAPI
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
# Code exécuté au démarrage
logger.info("Application démarrée")
yield
# Code exécuté à l'arrêt
logger.info("Application arrêtée")
app = FastAPI(lifespan=lifespan)Gestion des erreurs
Créer des gestionnaires d'exceptions personnalisés :
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
class AppException(Exception):
def __init__(self, status_code: int, detail: str):
self.status_code = status_code
self.detail = detail
app = FastAPI()
@app.exception_handler(AppException)
async def app_exception_handler(request: Request, exc: AppException):
return JSONResponse(
status_code=exc.status_code,
content={
"detail": exc.detail,
"timestamp": datetime.now(UTC).isoformat(),
},
)Documentation OpenAPI
FastAPI génère automatiquement la documentation OpenAPI, accessible par défaut à :
- Swagger UI :
/docs - ReDoc :
/redoc
Tests
Ajouter les dépendances de test :
uv add --dev pytest httpxUtiliser pytest avec le TestClient de FastAPI :
import pytest
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_get_cats():
response = client.get("/cats")
assert response.status_code == 200
assert isinstance(response.json(), list)
def test_create_cat():
response = client.post("/cats", json={"name": "Minou", "age": 3})
assert response.status_code == 201
assert response.json()["name"] == "Minou"Lancer les tests :
uv run pytestDockerfile
Exemple de Dockerfile multi-stage optimisé avec uv :
# Build
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
WORKDIR /app
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --locked --no-install-project --no-dev
COPY . /app
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --no-dev
# Runtime
FROM python:3.12-slim-bookworm
RUN groupadd --system --gid 999 nonroot \
&& useradd --system --gid 999 --uid 999 --create-home nonroot
COPY --from=builder --chown=nonroot:nonroot /app /app
ENV PATH="/app/.venv/bin:$PATH"
USER nonroot
WORKDIR /app
CMD ["fastapi", "run", "--host", "0.0.0.0", "app/main.py"]Du POC à la production
cf. POC to Prod pour une checklist complète de mise en production d'un projet FastAPI.
