D-OPEN

Comment créer une API REST avec FastAPI en 6 étapes, de l’installation à la validation automatique

Créer une API REST avec FastAPI, tutoriel en 6 étapes
Bryan

Bryan

Développeur et consultant open source · 4 septembre 2026 · 11 min de lecture

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 install suffit : 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
La sortie du terminal liste ce qui arrive avec le framework : 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 :

  1. L’import. On importe la classe FastAPI depuis le paquet installé à l’étape précédente.
  2. 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.
  3. 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.
  4. 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.

Fichier main.py de huit lignes affiché dans VS Code avec le décorateur @app.get barre oblique, la fonction async def root et le retour du dictionnaire message Hello World, et le terminal qui exécute la commande fastapi dev main.py
Huit lignes, dont trois vides. Le terminal exécute 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 :

  • TodoBase ne contient que title. C’est le strict nécessaire que le client doit fournir, et rien d’autre.
  • Todo hérite de TodoBase et y ajoute id et done. Ces deux champs sont produits côté serveur : le client n’a ni à les inventer, ni à les envoyer.
Code Python dans VS Code montrant la classe TodoBase avec le champ title de type str, la classe Todo qui en hérite avec les champs id de type UUID et done booléen à False, puis le dictionnaire todos typé dict UUID Todo initialisé vide
Le dictionnaire 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 todo

Trois paramètres méritent une explication, parce qu’ils portent l’essentiel de la valeur ajoutée du framework :

  1. 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.
  2. response_model=Todo sur la route de création décrit l’objet unique renvoyé après l’enregistrement, identifiant compris.
  3. status_code=201 remplace le 200 par défaut pour signaler la création d’une ressource, comme le veut la convention HTTP.
Code Python affichant la route app.get slash todos avec response_model égal list Todo et la fonction list_todos, puis la route app.post slash todos avec response_model Todo et status_code 201 et la fonction create_todo qui génère un uuid4 et stocke la tâche
Le paramètre 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 :

  1. Déplier le bloc POST /todos.
  2. Cliquer sur Try it out, ce qui rend le corps de requête modifiable.
  3. Remplacer la valeur de title par un intitulé réel, par exemple Apprendre FastAPI.
  4. Cliquer sur Execute.
Interface Swagger de FastAPI sur localhost:8000/docs, bloc POST /todos Create Todo déplié, avec le corps de requête au format application/json contenant le champ title en cours de saisie et les boutons Cancel et Reset
Le corps de requête est pré-rempli à partir du modèle 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.

Réponse de la requête GET sur /todos dans Swagger : commande curl équivalente, Request URL http://localhost:8000/todos, code 200 et corps de réponse listant trois tâches avec leur title, leur id au format UUID et done à false
Code 200 et liste complète. L’interface affiche aussi la commande 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
    }
  ]
}
Réponse d’erreur dans Swagger après l’envoi d’un titre numérique : commande curl avec title 123, code 422 Error Unprocessable Content et corps de réponse détaillant le type string_type, la localisation body title et le message Input should be a valid string
Code 422, champ fautif localisé, message explicite et valeur reçue rappelée. Aucune de ces informations n’a demandé la moindre ligne de code.

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. 1. Que récupère-t-on en installant fastapi[all] plutôt que fastapi seul ?

  2. 2. À quoi sert le décorateur @app.get("/") placé au-dessus d’une fonction ?

  3. 3. Pourquoi le modèle Todo hérite-t-il de TodoBase au lieu de tout redéclarer ?

  4. 4. Qu’apporte response_model=list[Todo] sur la route GET /todos ?

  5. 5. Où se teste l’API sans installer le moindre client HTTP supplémentaire ?

  6. 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.

Votre API mérite mieux qu’un dictionnaire en mémoire.

Nous concevons, sécurisons et maintenons des back-ends Python open source pour les entreprises françaises, de la première route à la mise en production.

Parlons de votre projet