Exposer trois routes en Python, c’est l’affaire d’une après-midi. Les rendre fiables, c’est autre chose : il faut vérifier que le client a bien envoyé une chaîne de caractères et non un entier, renvoyer un message d’erreur exploitable, tenir une documentation à jour, et éviter que le tout se transforme en trois cents lignes de contrôles écrits à la main.
C’est précisément le problème que FastAPI a été conçu pour régler. Le projet est né en 2018 sous la plume de Sebastián Ramírez, un développeur colombien qui travaillait alors sur des projets d’apprentissage automatique. À l’époque, deux options dominaient : Flask, simple mais sans structure, où validation et documentation restaient à la charge du développeur, et Django REST Framework, puissant mais lourd, avec beaucoup de code répétitif. Ni l’un ni l’autre n’exploitait vraiment les nouveautés du langage, en particulier les annotations de type et la syntaxe async. L’objectif affiché était de combiner la simplicité de Flask avec des performances comparables à celles de Node.js, en s’appuyant sur ces fonctionnalités modernes. Le pari a tenu : le dépôt dépasse aujourd’hui les 90 000 étoiles sur GitHub, et le framework tourne en production chez des acteurs comme Uber ou Netflix pour des API de données concurrentes et asynchrones.
Ce guide suit le chemin complet, dans l’ordre : isoler le projet, écrire la première route, décrire les données avec des modèles, déclarer les routes de lecture et de création, tester le tout dans la documentation générée automatiquement, puis vérifier que la validation fait bien son travail. À la fin, vous avez une petite API de gestion de tâches, documentée et validée, en une trentaine de lignes de Python. Coût : zéro, le framework est gratuit et open source.
L’essentiel à retenir
- • Un seul
pip installsuffit : la variante complète embarque Pydantic, Starlette et Uvicorn, le serveur ASGI sans lequel rien ne répond. - • Une route tient en trois lignes : un décorateur qui associe méthode HTTP et chemin, une fonction, un dictionnaire retourné que FastAPI convertit en JSON.
- • Les modèles Pydantic sont le cœur du dispositif : ils décrivent les données entrantes et sortantes, et servent à la fois de validateur, de sérialiseur et de source de documentation.
- • La documentation interactive est gratuite : elle est générée sur
/docsà partir du code, ce qui rend inutile l’installation d’un client HTTP externe pour les tests manuels. - • Une donnée du mauvais type renvoie un 422 détaillant le champ fautif, sans une seule ligne de validation écrite à la main.
Étape 1 : isoler le projet et installer FastAPI
La première commande n’a rien à voir avec FastAPI, et c’est pourtant celle qu’on regrette le plus d’avoir sautée. Un environnement virtuel isole les dépendances du projet et évite les conflits de versions entre les bibliothèques installées sur la machine. Deux projets Python qui réclament deux versions différentes de la même bibliothèque cohabitent sans problème dès lors que chacun a le sien.
# Création puis activation de l'environnement virtuel
python -m venv venv
source venv/bin/activate # macOS et Linux
venv\Scripts\activate # Windows
# Installation du framework et de ses dépendances
pip install "fastapi[all]"La variante entre crochets change tout. Elle ne se contente pas de poser le framework : elle installe la pile complète, à commencer par Uvicorn, le serveur ASGI qui fera effectivement tourner l’application. Sans lui, main.py reste un fichier Python qui ne répond à aucune requête.
![Fenêtre VS Code avec le fichier main.py contenant l’import de FastAPI et l’instanciation de l’application, et le terminal PowerShell affichant la commande pip install fastapi[all] suivie de la liste des paquets installés](/images/tutos/creer-api-rest-fastapi-6-etapes/etape-1-installation-fastapi.jpg)
pydantic pour les modèles, starlette pour la couche web, uvicorn pour le serveur.Prenez trente secondes pour lire cette liste plutôt que de la faire défiler. Elle constitue la surface de dépendances de votre projet, et c’est elle qu’il faudra surveiller par la suite : Starlette, sur lequel repose FastAPI, a déjà fait l’objet de correctifs de sécurité, comme le rappelle notre analyse de la faille BadHost qui touchait Starlette et FastAPI.
Étape 2 : écrire la première route et lancer le serveur
Tout se passe désormais dans un fichier main.py, à la racine du projet. Quatre éléments suffisent à obtenir une API qui répond :
- L’import. On importe la classe
FastAPIdepuis le paquet installé à l’étape précédente. - L’instance.
app = FastAPI()crée l’objet qui gérera l’ensemble des routes et des requêtes. C’est lui que le serveur ira chercher au démarrage. - Le décorateur.
@app.get("/")déclare un point d’entrée qui répond aux requêtes GET sur la racine de l’API. La méthode HTTP et le chemin tiennent dans cette seule ligne. - La fonction. Elle retourne un dictionnaire Python, que FastAPI convertit automatiquement en JSON. Aucune sérialisation à écrire.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}Reste à démarrer le serveur. La commande fastapi dev main.py lance le serveur de développement, avec rechargement automatique à chaque enregistrement du fichier.

fastapi dev main.py : le serveur écoute alors sur le port 8000.En ouvrant http://localhost:8000 dans un navigateur, on obtient {"message":"Hello World"}. Ce n’est pas spectaculaire, mais cela confirme trois choses d’un coup : l’environnement est correctement activé, le serveur ASGI tourne, et le routage fonctionne. Autant le vérifier maintenant plutôt qu’après avoir écrit cent lignes.
Étape 3 : décrire ses données avec des modèles Pydantic
On passe à une vraie API : une liste de tâches, avec lecture et création. Et c’est là que FastAPI se distingue d’un simple routeur HTTP. Plutôt que de vérifier à la main le contenu de chaque requête, on décrit la forme attendue des données, et le framework se charge du reste.
Deux imports supplémentaires suffisent : BaseModel depuis Pydantic pour définir les modèles, et uuid4 avec UUID pour générer des identifiants uniques.
from fastapi import FastAPI
from pydantic import BaseModel
from uuid import uuid4, UUID
app = FastAPI()
class TodoBase(BaseModel):
title: str
class Todo(TodoBase):
id: UUID
done: bool = False
todos: dict[UUID, Todo] = {}Le découpage en deux modèles mérite qu’on s’y arrête, parce que c’est lui qui rend le code lisible :
TodoBasene contient quetitle. C’est le strict nécessaire que le client doit fournir, et rien d’autre.Todohérite deTodoBaseet y ajouteidetdone. Ces deux champs sont produits côté serveur : le client n’a ni à les inventer, ni à les envoyer.

todos sert de stockage en mémoire. Pratique pour une démonstration, mais tout disparaît au redémarrage du serveur.Un mot sur ce dictionnaire, justement. Il tient lieu de base de données le temps du tutoriel, avec l’identifiant comme clé et l’objet comme valeur. C’est parfaitement acceptable pour apprendre, et parfaitement inacceptable en production : les données vivent dans la mémoire du processus, elles ne survivent ni à un redémarrage, ni à un déploiement sur plusieurs instances. Le jour où le projet devient sérieux, le sujet devient celui de déployer une base PostgreSQL en production.
Étape 4 : déclarer les routes avec response_model
Les modèles étant posés, les routes deviennent presque déclaratives. Deux suffisent pour couvrir la lecture et la création.
@app.get("/todos", response_model=list[Todo])
def list_todos():
return list(todos.values())
@app.post("/todos", response_model=Todo, status_code=201)
def create_todo(todo_data: TodoBase):
todo_id = uuid4()
todo = Todo(id=todo_id, title=todo_data.title)
todos[todo_id] = todo
return todoTrois paramètres méritent une explication, parce qu’ils portent l’essentiel de la valeur ajoutée du framework :
response_model=list[Todo]sur la route de lecture indique que la réponse est une liste de tâches. FastAPI valide la sortie, la sérialise en JSON et publie le schéma correspondant dans la documentation.response_model=Todosur la route de création décrit l’objet unique renvoyé après l’enregistrement, identifiant compris.status_code=201remplace le 200 par défaut pour signaler la création d’une ressource, comme le veut la convention HTTP.

todo_data: TodoBase de create_todo déclenche à lui seul la validation du corps de la requête entrante.Notez l’annotation todo_data: TodoBase dans la signature de la fonction de création. Elle ne sert pas qu’à documenter l’intention du développeur : c’est elle qui indique à FastAPI de lire le corps de la requête, de le valider contre le modèle et de refuser tout ce qui ne correspond pas. Une annotation de type Python, utilisée comme contrat d’interface. C’est toute l’idée du framework.
Un prototype qui fonctionne n’est pas une API de production.
Authentification, persistance, tests, supervision, déploiement : nos développeurs conçoivent et maintiennent des back-ends Python open source pour des entreprises françaises.
Parlons de votre APIÉtape 5 : tester l’API dans la documentation générée
Le réflexe classique consiste à ouvrir un client HTTP pour envoyer les premières requêtes. Inutile ici : FastAPI expose une documentation interactive sur http://localhost:8000/docs, générée à partir des annotations de type et des modèles écrits aux étapes précédentes. Toutes les routes y figurent, avec leur corps de requête, leur schéma de réponse et un bouton pour les exécuter.
La création d’une tâche se déroule en quatre gestes :
- Déplier le bloc POST /todos.
- Cliquer sur Try it out, ce qui rend le corps de requête modifiable.
- Remplacer la valeur de
titlepar un intitulé réel, par exemple Apprendre FastAPI. - Cliquer sur Execute.

TodoBase : seul le champ title apparaît, puisque c’est le seul que le client doit fournir.La réponse renvoie l’objet complet, avec l’identifiant unique généré par le serveur et le champ done à false. On peut ensuite dérouler GET /todos et exécuter la requête pour voir apparaître l’ensemble des tâches créées.

curl équivalente, pratique à recopier dans un script de test.Ce détail change la vie d’une équipe : la documentation ne se maintient pas, elle se déduit. Un champ qui change de type dans un modèle est immédiatement reflété dans le schéma affiché. Personne n’a besoin de penser à mettre à jour un fichier annexe, et surtout personne ne peut oublier de le faire.
Étape 6 : vérifier que la validation refuse les mauvaises données
Reste à tester ce qui compte vraiment : le comportement de l’API quand le client se trompe. Reprenez le bloc POST /todos, remplacez le titre par un entier, par exemple 123, et exécutez la requête.
La réponse ne laisse aucune place au doute :
{
"detail": [
{
"type": "string_type",
"loc": ["body", "title"],
"msg": "Input should be a valid string",
"input": 123
}
]
}
Quatre informations dans un seul objet : le type d’erreur, l’emplacement exact du champ fautif dans la requête, un message lisible et la valeur qui a été rejetée. Un développeur front-end qui reçoit cela sait immédiatement quoi corriger, et le message peut même être remonté tel quel dans une interface.
Comparez avec ce que représenterait le même niveau de détail écrit à la main, sur une API de vingt routes et quarante champs. C’est exactement ce travail que les modèles Pydantic suppriment.
Erreurs fréquentes au démarrage
Quatre pièges reviennent systématiquement sur un premier projet FastAPI, et aucun n’est difficile à éviter.
Travailler sans environnement virtuel. Tout fonctionne, jusqu’au jour où un second projet réclame une autre version d’une bibliothèque partagée. On passe alors une soirée à comprendre pourquoi un import qui marchait la veille échoue aujourd’hui. Une commande au démarrage évite le problème définitivement.
Oublier le serveur ASGI. FastAPI n’est pas un serveur : c’est un framework qui décrit des routes. Uvicorn est ce qui écoute réellement sur le port et transmet les requêtes. Une installation minimale sans lui produit un projet qui s’importe correctement et ne répond à personne.
Prendre le dictionnaire pour une base de données. Le stockage en mémoire est un raccourci de démonstration. Il donne l’illusion d’une API complète pendant toute la durée du tutoriel, puis perd tout au premier redémarrage. Autant le savoir avant de bâtir dessus.
Installer un client HTTP externe pour les premiers tests. La documentation sur /docs couvre déjà l’essentiel des tests manuels, et elle a l’avantage d’être toujours synchronisée avec le code. Un outil dédié garde son intérêt pour les scénarios complexes et les collections partagées, pas pour vérifier qu’une route répond.
Questions fréquentes
Faut-il déclarer toutes les routes en async ?
Non. Dans l’exemple suivi ici, la route racine est écrite avec async def alors que les deux routes de la todo-list utilisent un def classique, sans que le client voie la moindre différence. FastAPI accepte les deux formes et exécute les fonctions synchrones dans un pool de threads. Le mot-clé prend tout son sens quand la fonction attend réellement quelque chose, typiquement un appel réseau ou une requête vers un pilote de base de données asynchrone.
Pourquoi choisir FastAPI plutôt que Flask ou Django REST Framework ?
Parce que la validation, la sérialisation et la documentation sortent du code métier pour devenir des annotations de type. Flask reste imbattable de simplicité mais laisse ces trois chantiers entièrement au développeur ; Django REST Framework les couvre au prix d’une quantité notable de code répétitif. Cela dit, le choix du langage n’est pas gravé dans le marbre : pour une API taillée pour l’edge, notre guide sur créer une API REST avec Hono et Bun explore l’équivalent côté JavaScript.
D’où vient la documentation interactive ?
Du code lui-même. Les annotations de type, les modèles Pydantic et les response_model déclarés sur chaque route suffisent à produire un schéma OpenAPI, que FastAPI expose sous forme d’interface Swagger sur /docs. Il n’y a rien à écrire à part, donc rien à laisser dériver.
Que deviennent les données quand on redémarre le serveur ?
Elles disparaissent. Le dictionnaire utilisé ici vit dans la mémoire du processus, ce qui convient à une démonstration et à rien d’autre. Le passage à une base de données réelle est la première étape d’un projet destiné à la production, avant même l’authentification.
Quiz : avez-vous suivi ?
Six questions tirées des étapes ci-dessus. Une seule bonne réponse par question.
1. Que récupère-t-on en installant fastapi[all] plutôt que fastapi seul ?
2. À quoi sert le décorateur @app.get("/") placé au-dessus d’une fonction ?
3. Pourquoi le modèle Todo hérite-t-il de TodoBase au lieu de tout redéclarer ?
4. Qu’apporte response_model=list[Todo] sur la route GET /todos ?
5. Où se teste l’API sans installer le moindre client HTTP supplémentaire ?
6. Que renvoie l’API si on poste {"title": 123} alors que le modèle attend une chaîne ?
0 question sur 6 traitée.
Une trentaine de lignes de Python, deux modèles et deux routes : voilà ce qui sépare une idée d’une API documentée, validée et testable dans le navigateur. Ce qui reste à faire est d’un autre ordre, et c’est précisément là que se joue la différence entre un prototype et un service que l’on exploite : persistance, authentification, tests automatisés, journalisation et déploiement. Rien d’insurmontable, mais rien qui ne s’improvise non plus.
