Créer une API en quelques minutes avec FastAPI
FastAPI est un très bon outil pour développer rapidement des API en Python. Dans cet article, nous allons voir pourquoi le choix de ce framework et comment créer notre première application en quelques minutes.
- Python
- API
Pourquoi FastAPI 🚀
En consultant le site de FastAPI, la promesse est assez clair:
FastAPI framework, high performance, easy to learn, fast to code, ready for production
Le message est claire: performance et facilité. Le framework nous propose également une liste de features intéressante dont:
- l'intégration de l'auto-documentation se basant sur OpenAPI
- l'utilisation des dernières mises à jours de Python comme l'async/await et le typage
- l'injéction de dépendences
Vous pouvez consulter la liste complète des features ici.
Il a été également élu "New Tool of the Year" en 2021 par stackshare
Installation
On commence avec la création de l'environnement virtuel avec le module venv de Python.
# initialisation du venvpython3 -m venv venv# activation du venvsource venv/bin/activate
Concernant les requirements, nous avons besoin d'installer:
- Le framework FastAPI
- Uvicorn afin d'obtenir un serveur web permettant de gérer l'asynchrone. C'est un ASGI (Asynchronous Server Gateway Interface)
# création du fichier requirementstouch requirements.txt# écriture des dépendances dans le fichiercat <<EOT >> requirements.txtfastapi==0.73.0uvicorn==0.17.4EOT# installationpip install -r requirements.txt
Création de la première route
Maintenant que toutes les dépendances sont installées, nous pouvons créer notre première route de notre projet FastAPI.
Pour cela, nous créons un dossier app
et à l'intérieur de ce dernier, un fichier main.py
qui va contenir la déclaration de notre première route.
# ~/app/main.pyfrom fastapi import FastAPIapp = FastAPI()@app.get("/")async def read_home(): return {"Music": "Song"}
Nous pouvons maintenant lancer uvicorn, à la racine de notre projet:
uvicorn app.main:app --reload
Vous pouvez maintenant accéder à votre API en allant sur http://127.0.0.1:8000
et sur la documentation de votre route http://127.0.0.1:8000/docs
.
Actions
Ok, maintenant que notre projet fonctionne et que nous pouvons visualiser le résultat de notre première route, nous allons construire notre gestion des titres de musique avec des actions simples.
Avant de commencer, un peu de typage
C'est le bon moment pour introduire pydantic qui permet de nous faciliter la vie avec la validation de notre modèle en l'occurence Song
dans notre cas.
from typing import Listfrom pydantic import BaseModelclass Song(BaseModel): id: int title: str artist: str genre: List[str]# Notre liste de musiquessongs: List[Song] = [ Song( id=1, title='Gung Ho', artist='Shaka Ponk', genre=['Rock', 'Electronic rock'] ), Song( id=2, title='Rusty Fonky', artist='Shaka Ponk', genre=['Rock', 'Electronic rock'] ),]
On déclare notre modèle Song
étendu de BaseModel
avec les différents attributs. Puis nous créons en dessous notre variable songs qui est typé comme liste de Song
.
Lister les titres de musiques
Dans l'exemple suivant, la route retourne la collection de musique précédemment créée. response_model
permet de contrôler quel forme de réponse doit retourner /songs
.
@app.get("/songs", response_model=List[Song])async def get_songs(): return songs
Trouver une musique en fonction de son id
La route suivante filtre la collection de musique par son id. Notez le typage associé au paramètre song_id
, FastAPI lévera automatiquement une exception si le paramètre passé n'est pas de type int
.
@app.get("/songs/{song_id}", response_model=Song)async def get_song(song_id: int): # retourne le premier résultat positif dans les dictionnaires de la liste. song = next((s for s in songs if s['id'] == song_id), None) if not song: raise HTTPException(status_code=404, detail="Song not found") return song
Créer une nouvelle musique
Pour ajouter une nouvelle musique à notre collection, nous utilisons le verbe POST
. Un modèle SongCreate
a aussi été créé afin de valider le body envoyé.
Une fois que ce dernier est validé, on instancie une classe Song
avec les données validées puis nous y ajoutons un ID
unique pour notre collection. Song
est ensuite ajouté à notre collection de musique.
class SongCreate(BaseModel): title: str artist: str genre: List[str]@app.post("/songs", response_model=Song)async def get_songs(song: SongCreate): last_song_registered = max(songs, key=lambda s: s.id) new_id = last_song_registered.id + 1 new_song = Song(**song.dict(), id=new_id) # On ajoute la nouvelle chanson à notre collection songs.append(new_song) return new_song
Supprimer une musique de notre collection
Enfin il nous manque l'action de supprimer une musique par son ID
. Pour cela, nous utilisons le verbe DELETE
. Une exception est levée si l'index n'est pas trouvé dans la collection de musique.
@app.delete("/songs/{song_id}")async def delete_songs(song_id: int): index_song = next((index for (index, s) in enumerate(songs) if s.id == song_id), None) if index_song is None: raise HTTPException(status_code=404, detail=f"Song not found") del songs[index_song] return {}