FastAPI : le nouveau framework web pour vos API avec Python
Tags: python, programming, tutorial
Published: 2021-12-02 08:56:12

Il y a depuis longtemps deux frameworks web bien implantés dans le monde Python : [Django](https://www.djangoproject.com/) et [Flask](https://flask.palletsprojects.com/en/2.0.x/). 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](https://fastapi.tiangolo.com/). 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](https://www.jetbrains.com/lp/python-developers-survey-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]( https://fastapi.tiangolo.com/tutorial/#install-fastapi) se fait bien évidemment avec `pip` :

```bash
pip install fastapi[all]
```

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'`uvicorn` sera installé en même temps. Il nous servira de [serveur](https://www.uvicorn.org/) pour nos exemples.

## hello, world

Dans un dossier `myapi`, on crée un fichier `main.py` :

```python
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "hello, world"}
```

Ce code est très simple à comprendre. On crée une instance de `FastAPI` nommée `app`, 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 :

```bash
$> 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.
```

On utilise ici `uvicorn` comme serveur. On lui demande d'utiliser le module Python `main` (il faut donc exécuter la commande depuis le dossier où se trouve notre fichier `main.py`) et y chercher l'instance `app` de FastAPI.

On ouvre l'adresse indiquée dans un navigateur :

![hello world dans chrome](hello_world.png)

L'option `--reload` demande à `uvicorn` de recharger le site au fur et à mesure que le fichier `main.py` est modifié, sans avoir besoin de redémarrer l'application.

Pour tester cette fonctionnalité, on ajoute une nouvelle route `/about` dans à notre API :

```python
@app.get('/about')
async def about():
    return {"about": "This is my first api with FastAPI"}
```

Les logs de `uvicorn` montrent que les changements sont bien détectés et chargés :

```raw
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.
```

Cette nouvelle route est accessible par notre navigateur :

![about dans chrome](about.png)

## 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 :

```python
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}
```

On utilise `curl` pour tester nos nouvelles routes :

```bash
$ 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\"]"
```

## 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](https://github.com/OAI/OpenAPI-Specification), en récupérant les _docstrings_ des fonctions annotées. Ce schéma est disponible avec la route `/openapi.json` :

![schema OpenAPI en JSON dans Chrome](openapi_json.png)

Ensuite, il utilise [Swagger UI](https://swagger.io/tools/swagger-ui/) et [Redoc](https://github.com/Redocly/redoc) pour le rendu. Ces 2 versions de la documentation sont accessibles avec les routes `/docs` et `/redoc` respectivement.

![Doc avec Swagger UI](swagger_ui.png)

![Doc avec ReDoc](redoc.png)

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 `uvicorn` remettra à jour `/docs` et `/redoc` 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"](https://fastapi.tiangolo.com/benchmarks/) du site officiel pour cela.