Il y a depuis longtemps deux frameworks web bien implantés dans le monde Python : Django et Flask. Il y a des pros et des cons à chacun de ces frameworks, Django étant souvent présenté comme plus complet mais plus difficile à prendre en main, Flask comme étant plus simple pour déployer rapidement une API.
Depuis quelques mois, on entend de plus en plus parler d'un nouveau framework: FastAPI. Par exemple, JetBrains conduit tous les ans une enquête auprès de développeur.se.s Python et FastAPI a fait une entrée remarquée en 2020 :
FastAPI was introduced to the options for the first time with this iteration of the survey, and it appears to be the third most popular web framework for Python.
Ce framework se veut moderne, rapide, prêt au déploiement. Voyons voir ça !
Installation
L'installation se fait bien évidemment avec <span class="css-span">pip</span> :
<pre><code>pip install fastapi[all]<code><pre>
Petit truc exotique quand même : des crochets dans le nom du paquet. Histoire de mettre un peu de paillettes dans la vie des développeurs.
Notez qu'<span class="css-span">uvicorn</span> sera installé en même temps. Il nous servira de serveur pour nos exemples.
hello, world
Dans un dossier <span class="css-span">myapi</span>, on crée un fichier <span class="css-span">main.py</span> :
<pre><code>from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "hello, world"}<code><pre>
Ce code est très simple à comprendre. On crée une instance de <span class="css-span">FastAPI</span> nommée <span class="css-span">app</span>, puis on ajoute une route vers la racine avec une opération GET, grâce une élégante syntaxe à base d'annotations.
On lance un serveur pour exposer notre API :
<pre><code>$> cd myapi
$> uvicorn main:app --reload
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [1972] using watchgod
INFO: Started server process [16100]
INFO: Waiting for application startup.
INFO: Application startup complete.<code><pre>
On utilise ici <span class="css-span">uvicorn</span> comme serveur. On lui demande d'utiliser le module Python <span class="css-span">main</span> (il faut donc exécuter la commande depuis le dossier où se trouve notre fichier <span class="css-span">main.py</span>) et y chercher l'instance <span class="css-span">app</span> de FastAPI.
On ouvre l'adresse indiquée dans un navigateur :
L'option <span class="css-span">--reload</span> demande à <span class="css-span">uvicorn</span> de recharger le site au fur et à mesure que le fichier <span class="css-span">main.py</span> est modifié, sans avoir besoin de redémarrer l'application.
Pour tester cette fonctionnalité, on ajoute une nouvelle route /about dans à notre API :
<pre><code>@app.get('/about')
async def about():
return {"about": "This is my first api with FastAPI"}<code><pre>
Les logs de <span class="css-span">uvicorn</span> montrent que les changements sont bien détectés et chargés :
<span class="css-span">
WARNING: WatchGodReload detected file change in '['C:\\myapi\\main.py']'. Reloading...
INFO: Started server process [3708]
INFO: Waiting for application startup.
INFO: Application startup complete.</span>
Cette nouvelle route est accessible par notre navigateur :
Route avec paramètres
Il est bien sûr possible d'ajouter des paramètres à une route.
Pour l'exemple, on crée une variable users avec la liste des utilisateurs. On ajoute une route de type GET pour récupérer cette liste, et une route de type POST pour ajouter un élément à cette liste :
<pre><code>users = ['pierre', 'natacha', 'benjamin']
@app.get('/users/list')
async def users_list():
"""Get the list of available users."""
return json.dumps(users)
@app.post('/users/add/{name}')
async def users_add(name: str):
"""Add a new user
. :param name: the name of the new user
"""
users.append(name)
return {'added': name}<code><pre>
On utilise <span class="css-span">curl</span> pour tester nos nouvelles routes :
<pre><code>$ curl --request GET http://127.0.0.1:8000/users/list
"[\"pierre\", \"natacha\", \"benjamin\"]"
$ curl --request POST http://127.0.0.1:8000/users/add/florian
{"added":"florian"}
$ curl --request POST http://127.0.0.1:8000/users/add/denis
{"added":"denis"}
$ curl --request POST http://127.0.0.1:8000/users/add/bertrand
{"added":"bertrand"}
$ curl --request GET http://127.0.0.1:8000/users/list
"[\"pierre\", \"natacha\", \"benjamin\", \"florian\", \"denis\", \"bertrand\"]"<code><pre>
Documentation de l'API
FastAPI se charge de générer la documentation de l'API. Cette documentation est accessible grâce à des routes ajoutées automatiquement à l'API.
Pour cela, il génère un schéma OpenAPI, en récupérant les docstrings des fonctions annotées. Ce schéma est disponible avec la route <span class="css-span">/openapi.json</span> :
Ensuite, il utilise Swagger UI et Redoc pour le rendu. Ces 2 versions de la documentation sont accessibles avec les routes <span class="css-span">/docs</span> et <span class="css-span">/redoc</span> respectivement.
On a ainsi la liste des routes, avec leurs types, leurs paramètres, leurs documentations. On peut tester notre API directement depuis ces pages. Et comme tout est en auto-reload, on peut faire nos modifications au fur et à mesure et <span class="css-span">uvicorn</span> remettra à jour <span class="css-span">/docs</span> et <span class="css-span">/redoc</span> pour qu'on puisse retester immédiatement.
Conclusion
Mes premières impressions sur FastAPI sont très positives. La syntaxe est proche de celle de Flask, peut-être encore plus simple et élégante. La génération automatique et out-of-the-box de la documentation est vraiment cool ! Je ne me suis pas intéressé à l'aspect performances du framework, pour me concentrer sur son utilisation. Je vous laisse consulter la section "Benchmarks" du site officiel pour cela.
Que la vie de Pierre, expert embarqué Younup, serait terne sans les variadic templates et les fold expressions de C++17. Heureusement pour lui, Python a tué l'éternel débat sur l’emplacement de l’accolade : "alors, à la fin de la ligne courante ou au début de la ligne suivante ?"
Homme de terrain, il est aussi à l’aise au guidon de son VTT à sillonner les chemins de forêt, dans une salle de concert de black metal ou les mains dans les soudures de sa carte électronique quand il doit déboguer du code (bon ça, il aime moins quand même !)
Son vœu pieux ? Il hésite encore... Faire disparaitre le C embarqué au profit du C++ embarqué ? Ou stopper la génération sans fin d'entropie de son bureau.