Cours Python · Module 6: Async et FastAPI

FastAPI - une API asynchrone moderne

6 min de lecture
Dans cette leçon10

Content de te revoir ! Ici Darwin avec FastAPI - le framework le plus moderne pour construire des API en Python !

Dans la leçon précédente, tu as appris async/await. Maintenant, on va mettre ces connaissances en pratique avec FastAPI - un framework 3 à 5 fois plus rapide que Flask, doté d'une validation automatique et qui génère la documentation tout seul !

Analogie safari : Flask, c'est un circuit de safari traditionnel - éprouvé et fiable. FastAPI, c'est un safari avec des drones - rapide, moderne, avec GPS et suivi automatique des animaux !

Qu'est-ce que FastAPI ?

FastAPI est un framework moderne et rapide pour construire des API, créé par Sebastián Ramírez en 2018.

Fonctionnalités clés : Rapide - comparable à Node.js/Go (grâce à Starlette) Async natif - support complet de async/await Type hints - utilise les annotations de types Validation automatique - Pydantic prêt à l'emploi Docs automatiques - Swagger UI + ReDoc automatiquement Python moderne - Python 3.7+ requis

Installer FastAPI

1pip install fastapi[all]  # Tout (Uvicorn, Pydantic, etc.)
2
3# Ou au minimum :
4pip install fastapi uvicorn[standard]

Uvicorn = serveur ASGI (comme Gunicorn pour WSGI)

Hello World avec FastAPI

1# main.py
2from fastapi import FastAPI
3
4app = FastAPI()
5
6@app.get("/")
7async def root():
8    return {"message": "Bonjour Safari !"}
9
10# Lance : uvicorn main:app --reload

Lance :

1uvicorn main:app --reload

Sortie :

1INFO: Uvicorn running on http://127.0.0.1:8000
2INFO: Application startup complete.

Ouvre :

FastAPI vs Flask

FastAPIFlask
Async natifSync (async possible, mais difficile)
Type hints requisOptionnels
Validation automatique (Pydantic)Manuelle (Flask-WTF)
Docs automatiques (Swagger/ReDoc)Aucune
3 à 5 fois plus rapidePlus lent
Python moderne (3.7+)Anciennes versions OK
API-firstApplis web + API

Quand FastAPI ? API REST, microservices, E/S async Quand Flask ? Applis web avec templates, projets plus petits

Les endpoints de base

1from fastapi import FastAPI
2
3app = FastAPI(title="Safari Database API", version="1.0.0")
4
5# GET - récupérer des données
6@app.get("/species")
7async def list_species():
8    return [
9        {"id": 1, "name": "Lion", "population": 120},
10        {"id": 2, "name": "Elephant", "population": 450}
11    ]
12
13# GET avec paramètre de chemin
14@app.get("/species/{species_id}")
15async def get_species(species_id: int):
16    return {"id": species_id, "name": "Lion", "population": 120}
17
18# POST - ajouter des données
19@app.post("/species")
20async def create_species(name: str, population: int):
21    return {"id": 3, "name": name, "population": population, "status": "created"}
22
23# PUT - mettre à jour
24@app.put("/species/{species_id}")
25async def update_species(species_id: int, name: str, population: int):
26    return {"id": species_id, "name": name, "population": population}
27
28# DELETE - supprimer
29@app.delete("/species/{species_id}")
30async def delete_species(species_id: int):
31    return {"id": species_id, "status": "deleted"}

Teste :

1# GET
2curl http://localhost:8000/species
3
4# POST
5curl -X POST "http://localhost:8000/species?name=Cheetah&population=7100"

Les query parameters - validation automatique

FastAPI analyse automatiquement les query params et valide les types !

1from fastapi import FastAPI
2
3app = FastAPI()
4
5@app.get("/species")
6async def list_species(
7    skip: int = 0,
8    limit: int = 10,
9    habitat: str | None = None,
10    endangered: bool = False
11):
12    # FastAPI automatiquement :
13    # - analyse ?skip=10&limit=5&habitat=savanna&endangered=true
14    # - convertit les types (str → int, str → bool)
15    # - valide (skip doit être un int !)
16
17    return {
18        "skip": skip,
19        "limit": limit,
20        "habitat": habitat,
21        "endangered": endangered,
22        "results": ["Lion", "Elephant"]
23    }

Teste :

1/species                              → skip=0, limit=10, habitat=None, endangered=False
2/species?skip=20&limit=5              → skip=20, limit=5
3/species?habitat=savanna&endangered=1 → habitat="savanna", endangered=True
4/species?skip=abc                     → ERREUR : erreur de validation !

FastAPI génère automatiquement les erreurs de validation :

1{
2  "detail": [
3    {
4      "loc": ["query", "skip"],
5      "msg": "value is not a valid integer",
6      "type": "type_error.integer"
7    }
8  ]
9}

Les response models - des réponses structurées

1from fastapi import FastAPI
2from pydantic import BaseModel
3
4app = FastAPI()
5
6class Species(BaseModel):
7    id: int
8    name: str
9    scientific_name: str
10    population: int
11    endangered: bool = False
12
13@app.get("/species/{species_id}", response_model=Species)
14async def get_species(species_id: int):
15    return Species(
16        id=species_id,
17        name="Lion",
18        scientific_name="Panthera leo",
19        population=120,
20        endangered=True
21    )

Avantages de response_model :

  • Sérialisation automatique en JSON
  • Validation de la sortie
  • La documentation montre automatiquement la structure
  • Sécurité des types

Une API Safari complète - exemple

1# main.py
2from fastapi import FastAPI, HTTPException
3from pydantic import BaseModel, Field
4from typing import List
5
6app = FastAPI(
7    title="Safari Database API",
8    description="API pour gérer les espèces sauvages d'Afrique",
9    version="1.0.0"
10)
11
12# Modèles
13class SpeciesBase(BaseModel):
14    name: str = Field(..., min_length=2, max_length=100)
15    scientific_name: str
16    population: int = Field(..., ge=0)
17    habitat: str
18
19class SpeciesCreate(SpeciesBase):
20    pass
21
22class Species(SpeciesBase):
23    id: int
24
25    class Config:
26        from_attributes = True
27
28# Base de données en mémoire
29species_db: dict[int, Species] = {
30    1: Species(id=1, name="Lion", scientific_name="Panthera leo", population=120, habitat="savanna"),
31    2: Species(id=2, name="Elephant", scientific_name="Loxodonta africana", population=450, habitat="savanna"),
32}
33next_id = 3
34
35# Endpoints
36@app.get("/")
37async def root():
38    return {"message": "Safari Database API", "version": "1.0.0"}
39
40@app.get("/species", response_model=List[Species])
41async def list_species(skip: int = 0, limit: int = 10, habitat: str | None = None):
42    """Liste toutes les espèces avec pagination"""
43    results = list(species_db.values())
44
45    if habitat:
46        results = [s for s in results if s.habitat == habitat]
47
48    return results[skip : skip + limit]
49
50@app.get("/species/{species_id}", response_model=Species)
51async def get_species(species_id: int):
52    """Récupère une espèce précise par son ID"""
53    if species_id not in species_db:
54        raise HTTPException(status_code=404, detail=f"Species {species_id} not found")
55
56    return species_db[species_id]
57
58@app.post("/species", response_model=Species, status_code=201)
59async def create_species(species: SpeciesCreate):
60    """Crée une nouvelle espèce"""
61    global next_id
62
63    new_species = Species(id=next_id, **species.dict())
64    species_db[next_id] = new_species
65    next_id += 1
66
67    return new_species
68
69@app.put("/species/{species_id}", response_model=Species)
70async def update_species(species_id: int, species: SpeciesCreate):
71    """Met à jour une espèce existante"""
72    if species_id not in species_db:
73        raise HTTPException(status_code=404, detail="Species not found")
74
75    updated = Species(id=species_id, **species.dict())
76    species_db[species_id] = updated
77
78    return updated
79
80@app.delete("/species/{species_id}")
81async def delete_species(species_id: int):
82    """Supprime une espèce"""
83    if species_id not in species_db:
84        raise HTTPException(status_code=404, detail="Species not found")
85
86    del species_db[species_id]
87    return {"status": "deleted", "id": species_id}

Lance :

1uvicorn main:app --reload

Teste l'API :

1# Liste
2curl http://localhost:8000/species
3
4# Espèce précise
5curl http://localhost:8000/species/1
6
7# Ajouter
8curl -X POST http://localhost:8000/species \
9  -H "Content-Type: application/json" \
10  -d '{"name":"Cheetah","scientific_name":"Acinonyx jubatus","population":7100,"habitat":"savanna"}'
11
12# Mettre à jour
13curl -X PUT http://localhost:8000/species/1 \
14  -H "Content-Type: application/json" \
15  -d '{"name":"Lion","scientific_name":"Panthera leo","population":150,"habitat":"savanna"}'
16
17# Supprimer
18curl -X DELETE http://localhost:8000/species/3

La documentation automatique

FastAPI génère automatiquement une documentation interactive !

Swagger UI : http://localhost:8000/docs

  • Tests interactifs de l'API
  • Bouton « Try it out » pour chaque endpoint
  • Formulaires automatiques

ReDoc : http://localhost:8000/redoc

  • Documentation lisible
  • Idéale pour les développeurs

OpenAPI JSON : http://localhost:8000/openapi.json

  • Schéma OpenAPI 3.0 brut
  • Pour les outils externes

Récapitulatif

Dans cette leçon, tu as appris :

  • Ce qu'est FastAPI et pourquoi l'utiliser
  • Installer FastAPI + Uvicorn
  • Hello World et les endpoints de base
  • GET, POST, PUT, DELETE
  • Les paramètres de chemin et les query parameters
  • Les response models avec Pydantic
  • HTTPException pour les erreurs
  • La documentation automatique (Swagger/ReDoc)
  • Une API Safari Database complète

Prochaine leçon : Darwin va te montrer Pydantic v2 - un puissant système de validation de données avec FastAPI !

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Comment décrirais-tu le mieux FastAPI ?

  2. 2. Comment installe-t-on FastAPI avec un serveur ASGI ?

Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Tri horizontal

    Comment crée-t-on une instance d'application FastAPI ?

  • Tri vertical

    Ordonne une application FastAPI minimale :

  • Clique dans l'ordre

    Ordonne la commande pour démarrer le serveur uvicorn :

  • Éditeur de code

    Écris un endpoint async GET /species qui renvoie une liste de dictionnaires contenant des espèces.

Articles utiles