Lance-toi en Python FastAPI : ton premier pas concret aujourd'hui

Python FastAPI : l'essentiel en un article — vrai code, schémas et étapes concrètes, extraits d'un cours de 32 leçons.

Lance-toi en Python FastAPI : ton premier pas concret aujourd'hui

La meilleure façon d'apprendre Python FastAPI, c'est de faire. Cet article te met le pied à l'étrier avec des extraits pratiques tirés d'un cours de 32 leçons — de quoi obtenir un premier résultat dès aujourd'hui.

tl;dr
  • Introduction et installation
  • Routes et methodes
  • Pydantic et validation
  • Dependency injection
  • Base de donnees
~$ cat ./parcours.md # Python FastAPI — 10 chapitres
01
Introduction et installation
→ Pourquoi FastAPI ?→ Installer FastAPI et uvicorn+ 1 autres leçons
02
Routes et méthodes
→ Path params et query params→ POST, PUT, PATCH, DELETE+ 1 autres leçons
03
Pydantic et validation
→ BaseModel pour les requetes→ Validators et constraints+ 2 autres leçons
04
Dependency injection
→ Depends - le principe→ Dependances avec yield+ 1 autres leçons
05
Base de données
→ SQLAlchemy avec FastAPI→ CRUD complet+ 1 autres leçons
06
Authentification JWT
→ OAuth2 + JWT→ Roles et permissions+ 1 autres leçons
07
Async et performance
→ async vs sync→ BackgroundTasks+ 1 autres leçons
08
Tests
→ TestClient et pytest→ Override dependencies+ 1 autres leçons
🏁
Projet final (+ 2 chapitres en chemin)
→ Tu repars avec un projet concret et démontrable

Pourquoi FastAPI ?

NOTEObjectif — Comprendre ce qui distingue FastAPI des autres frameworks Python et quand le choisir.

FastAPI en 1 phrase

Framework Python moderne pour construire des APIs REST rapides, typees et avec documentation auto, base sur Starlette (ASGI) et Pydantic.

Les 4 super-pouvoirs

PouvoirDetail
PerformanceAsync natif via ASGI/uvicorn, comparable a Node/Go
Type safetyType hints Python -> validation runtime via Pydantic
Docs autoSwagger UI + ReDoc generes automatiquement
Dev experienceAuto-completion IDE, erreurs claires, dependances injectables

Comparaison Flask vs FastAPI vs Django

CritereFlaskFastAPIDjango
StyleMicroMicro (focus API)Full-stack
Async natifNon (extensions)OUIPartiel (DRF non)
Type hintsNonOUI (Pydantic)Non
Docs autoNonOUI (Swagger)Via DRF (manuel)
ORM inclusNonNonOUI (Django ORM)
ApprentissageTres facileFacileModere
Perfs (req/s)~5k~20-30k~3k

Petit exemple : "Hello World"

output
# Flask
from flask import Flask
app = Flask(__name__)

@app.route("/items/<int:item_id>")
def get_item(item_id):
    return {"id": item_id}
output
# FastAPI
from fastapi import FastAPI
app = FastAPI()

@app.get("/items/{item_id}")
def get_item(item_id: int):
    return {"id": item_id}
# + validation auto, doc auto, type hints

Quand choisir FastAPI ?

TIPOUI
  • Backend API REST (microservices)
  • Servir un modele ML
  • Besoin de perfs (async)
  • Equipe qui apprecie le typage
  • Documentation OpenAPI requise
WARNINGNON
  • App full-stack avec templates HTML lourds (Django)
  • Petit script ponctuel (Flask suffit)
  • Equipe deja productive avec un autre framework

Qui utilise FastAPI ?

L'ecosysteme

ComposantRole
StarletteFramework ASGI sous-jacent
PydanticValidation et serialisation
uvicornServeur ASGI (dev)
gunicornProcess manager (prod)
SQLAlchemyORM (commun)
AlembicMigrations DB

Resume

NOTEA retenir
  • FastAPI = perf + type safety + docs auto
  • Base sur Starlette (ASGI) et Pydantic
  • Ideal pour APIs et microservices modernes
  • Async natif, ~5x plus rapide que Flask

Prochaine etape : Partie 2 — Installer FastAPI

Deploiement Docker et tests

Projet final • Docker • tests • CI/CD

NOTEObjectif — Finaliser le projet : tests pytest, Dockerfile, docker-compose, CI GitHub Actions.

Tests d'integration cles

output
# tests/test_posts.py
import pytest

def test_create_post_requires_auth(client):
    r = client.post("/posts", json={"title": "Test",
                                  "content": "Hello world"})
    assert r.status_code == 401

def test_create_post(client, auth_headers):
    r = client.post("/posts", headers=auth_headers, json={
        "title": "Mon premier post",
        "content": "Contenu interessant ici",
        "tags": ["python", "fastapi"],
        "published": True
    })
    assert r.status_code == 201
    data = r.json()
    assert data["slug"] == "mon-premier-post"
    assert len(data["tags"]) == 2

def test_list_posts_only_published(client, auth_headers):
    client.post("/posts", headers=auth_headers,
                json={"title": "Draft", "content": "...", "published": False})
    client.post("/posts", headers=auth_headers,
                json={"title": "Published", "content": "...", "published": True})
    
    r = client.get("/posts")
    assert len(r.json()["items"]) == 1

def test_only_author_can_delete(client):
    h1 = make_user_and_login(client, "alice", "alice@x.com")
    h2 = make_user_and_login(client, "bob", "bob@x.com")
    
    r = client.post("/posts", headers=h1,
                     json={"title": "Mien", "content": "...", "published": True})
    slug = r.json()["slug"]
    
    # Bob essaie de supprimer le post d'Alice
    r = client.delete(f"/posts/{slug}", headers=h2)
    assert r.status_code == 403
    
    # Alice peut
    r = client.delete(f"/posts/{slug}", headers=h1)
    assert r.status_code == 204

conftest.py final

output
import pytest
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.main import app
from app.db import Base, get_db

TestEngine = create_engine("sqlite:///./test.db",
                            connect_args={"check_same_thread": False})
TestSession = sessionmaker(bind=TestEngine)

def override_db():
    db = TestSession()
    try:
        yield db
    finally:
        db.close()

app.dependency_overrides[get_db] = override_db

@pytest.fixture(autouse=True)
def reset_db():
    Base.metadata.create_all(bind=TestEngine)
    yield
    Base.metadata.drop_all(bind=TestEngine)

@pytest.fixture
def client():
    return TestClient(app)

@pytest.fixture
def auth_headers(client):
    client.post("/auth/register", json={
        "email": "test@x.com", "username": "test",
        "password": "secret123"
    })
    r = client.post("/auth/login", data={
        "username": "test@x.com", "password": "secret123"
    })
    return {"Authorization": f'Bearer {r.json()["access_token"]}'}

Dockerfile

output
FROM python:3.11-slim AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

FROM python:3.11-slim
RUN useradd -m app
USER app
WORKDIR /home/app
ENV PATH=/home/app/.local/bin:$PATH

COPY --from=builder /root/.local /home/app/.local
COPY --chown=app:app . .

EXPOSE 8000
HEALTHCHECK CMD curl -fsS http://localhost:8000/health || exit 1

ENTRYPOINT ["./entrypoint.sh"]
CMD ["gunicorn", "-c", "gunicorn.conf.py", "app.main:app"]

docker-compose.yml

output
services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: postgresql://blog:pwd@db/blogdb
      SECRET_KEY: change-me-in-prod
      REFRESH_SECRET_KEY: change-me-too
    depends_on:
      db: {condition: service_healthy}
    restart: unless-stopped
  
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: blog
      POSTGRES_PASSWORD: pwd
      POSTGRES_DB: blogdb
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U blog"]
      interval: 5s
      retries: 5

volumes:
  pgdata:

CI/CD GitHub Actions

output
# .github/workflows/ci.yml
name: CI
on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: {python-version: "3.11"}
      - run: pip install -r requirements.txt
      - run: pip install pytest pytest-cov httpx
      - run: pytest --cov=app --cov-report=xml
      - uses: codecov/codecov-action@v4
  
  build:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == "refs/heads/main"
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v5
        with:
          push: true
          tags: ghcr.io/${{ github.repository }}:latest

Lancer en prod

output
# Local complet
docker-compose up -d
docker-compose logs -f api
docker-compose exec api alembic upgrade head

# Visiter
#   http://localhost:8000/docs
#   http://localhost:8000/redoc

WebSockets avec FastAPI

NOTEObjectif — Construire un chat en temps reel avec WebSocket natif FastAPI.

Hello WebSocket

output
from fastapi import FastAPI, WebSocket, WebSocketDisconnect

app = FastAPI()

@app.websocket("/ws")
async def websocket_endpoint(ws: WebSocket):
    await ws.accept()
    try:
        while True:
            data = await ws.receive_text()
            await ws.send_text(f"Echo: {data}")
    except WebSocketDisconnect:
        print("Client deconnecte")

Test rapide avec wscat

output
npm install -g wscat
wscat -c ws://localhost:8000/ws
> Hello
< Echo: Hello

Connection Manager

output
class ConnectionManager:
    def __init__(self):
        self.active: list[WebSocket] = []
    
    async def connect(self, ws: WebSocket):
        await ws.accept()
        self.active.append(ws)
    
    def disconnect(self, ws: WebSocket):
        self.active.remove(ws)
    
    async def broadcast(self, message: str):
        for connection in self.active:
            try:
                await connection.send_text(message)
            except:
                pass

manager = ConnectionManager()

Chat broadcast

output
@app.websocket("/chat/{username}")
async def chat(ws: WebSocket, username: str):
    await manager.connect(ws)
    await manager.broadcast(f">> {username} a rejoint")
    
    try:
        while True:
            msg = await ws.receive_text()
            await manager.broadcast(f"[{username}] {msg}")
    except WebSocketDisconnect:
        manager.disconnect(ws)
        await manager.broadcast(f"<< {username} a quitte")

Send JSON

output
@app.websocket("/json")
async def json_ws(ws: WebSocket):
    await ws.accept()
    try:
        while True:
            data = await ws.receive_json()
            await ws.send_json({"echo": data, "server_time": str(datetime.now())})
    except WebSocketDisconnect:
        pass

Authentification WebSocket

output
from fastapi import WebSocketException, status

@app.websocket("/ws")
async def ws(ws: WebSocket, token: str):
    try:
        payload = decode_token(token)
        user_id = payload["sub"]
    except:
        raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION)
    
    await ws.accept()
    await ws.send_text(f"Connecte en tant que {user_id}")
    ...

# Client : ws://localhost:8000/ws?token=eyJ...
va-plus-loin

Cet article couvre les extraits les plus utiles — le cours complet Python FastAPI (10 chapitres, 32 leçons, exercices corrigés et projet final) t'emmène jusqu'au bout.

./acceder-au-cours-complet cours gratuit : Vibe Coding

FAQ

Combien de temps pour apprendre Python FastAPI ?
Avec une progression structurée (10 chapitres, 32 leçons courtes et pratiques), on atteint un niveau opérationnel en quelques semaines à raison de 30 à 60 minutes par jour. L'important est de pratiquer chaque notion immédiatement.
Faut-il des prérequis ?
Des bases en informatique suffisent. Si tu sais utiliser un terminal et lire du code simple, tu es prêt.
Par où commencer concrètement ?
Reproduis les commandes de cet article, puis suis le cours complet Python FastAPI : il enchaîne les 32 leçons dans l'ordre, avec exercices et projet final.

📬 Tu veux recevoir ce type de guide chaque semaine ? Abonne-toi gratuitement — code réel, zéro blabla.