Introduction à FastAPI (Python) : Partie 3

ericlecodeur

Eric Le Codeur

Posted on November 29, 2021

Introduction à FastAPI (Python) : Partie 3

Voici une série d'articles qui vous permettra de créer une API en Python avec FastAPI.

Je vais publier un nouvel article régulièrement et petit à petit vous apprendrez tout ce qu'il y a à savoir sur FastAPI

Pour ne rien manquer suivez-moi sur twitter : https://twitter.com/EricLeCodeur


"Create"

Jusqu’à présent nous avons couvert le READ du CRUD, voyons maintenant le "Create".

Ici il ne s'agit plus de lire des données mais d'en créer une nouvelle.

Pour créer un nouveau produit nous devons envoyer de l'information au serveur, et pour ce faire nous aurons besoin d'utiliser l'action HTTP POST

POST www.example.com/products
Enter fullscreen mode Exit fullscreen mode

L'action POST permet d'envoyer des données du navigateur vers le serveur.

Exemple

Nous allons faire un exemple et ajouter le produit "MacBook" à notre liste de produit existant.

Voici le résultat visé :

products = [
    {"id": 1, "name": "iPad", "price": 599},
    {"id": 2, "name": "iPhone", "price": 999},
    {"id": 3, "name": "iWatch", "price": 699},
        {"id": 4, "name": "MacBook", "price": 1299},
]
Enter fullscreen mode Exit fullscreen mode

Pour faire un POST avec FastAPI voici le code que vous devez utiliser

@app.post("/products")
def create_product(product: dict, response: Response):
    product["id"] = len(products) + 1
    products.append(product)
    response.status_code = 201
    return product
Enter fullscreen mode Exit fullscreen mode

Reprenons ce code ligne par ligne:

@app.post("/products") 
Enter fullscreen mode Exit fullscreen mode

Ce code permet de spécifier à FastAPI que cette requête sera une action POST "/products"

def create_product(product: dict, response: Response):
Enter fullscreen mode Exit fullscreen mode

Ce code permet de définir la fonction qui sera lancée lorsque le serveur va recevoir la requête "POST /products"

La fonction contient deux paramètres, un pour le produit et l'autre pour une référence à l'objet réponse.

À noter que lorsque l'on fait une action POST les données à envoyer devront être inclus dans le body de la requête et devront l'être en format texte JSON.

Dans cet exemple on voit toute la puissance et facilité de FastAPI. FastAPI sait qu'il s'agit d'une requête POST, Il va donc automatiquement prendre le JSON qui se trouve dans le body de la requête, le convertir en dictionnaire Python et placer le résultat dans le premier paramètre (product).

Nous pouvons donc ensuite utiliser ce paramètre "product" pour ajouter le produit à notre liste de produit.

product["id"] = len(products) + 1
products.append(product)
Enter fullscreen mode Exit fullscreen mode

Une fois que le produit a été ajouté avec succès, il faut retourner le code d'état approprié.

response.status_code = 201
Enter fullscreen mode Exit fullscreen mode

Ce code permet de retourner le code d'état 201 (Created). Qui indique que le produit est bel et bien créé avec succès.

Enfin, par convention le nouveau produit est toujours retourné avec la réponse.

return product
Enter fullscreen mode Exit fullscreen mode

Tester une action HTTP Post

Les navigateurs web ne permettent pas de lancer une requête POST. Donc il ne sera pas possible de tester ce nouvel API à partir du navigateur.

Nous avons un premier API, nous devons maintenant le tester.

Les navigateurs web ne permettent pas de lancer une requête POST. Donc il ne sera pas possible de tester ce nouvel API à partir du navigateur.

Il existe plusieurs autres façons de le faire. Nous pourrions utiliser un logiciel comme Postman et tester nos requêtes à partir de Postman.

Un des avantages de FastAPI c'est qui produit automatiquement la documentation de notre API. Cette documentation permet aux usagers de comprendre comment utiliser notre API, quels sont les chemins URL disponible et également de lancer des requêtes.

Nous allons donc pouvoir utiliser la documentation afin de tester notre API

Comme mentionné, dans le cas de notre API la documentation est créée automatiquement. Pour la consulter, lancer le serveur

$ uvicorn first-api:app --reload

Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Enter fullscreen mode Exit fullscreen mode

et visiter /docs :

http://127.0.0.1:8000/docs
Enter fullscreen mode Exit fullscreen mode

La documentation va s'afficher à l'écran !

Image description

Pour tester un chemin URL c'est très simple, il suffit de cliquer sur le nom du chemin et FastAPI dévoilera une interface permettant de tester ce chemin.

Par exemple si vous cliquez sur POST/products vous verrez apparaître

Image description

Vous pouvez ensuite cliquer sur "Try it out", saisir les données à ajouter dans la section "Request body" (voir exemple ici bas)
Image description

Les données sont en format texte JSON

{"name": "MacBook", "price": 1299}
Enter fullscreen mode Exit fullscreen mode

Enfin appuyer sur "Execute", la doc envoyer la requête POST à votre serveur FastAPI

Le code d'état de la réponse ainsi que la réponse en tant que tel seront également affichés dans la page.

Image description

Cette documentation automatique est encore une autre belle démonstration de la puissance et facilité de FastAPI.

Si vous désirez, vous pouvez cliquer sur tous les chemins un après l'autre afin de les tester.

Validation

Reprenons le dernier exemple que nous avons créé

@app.post("/products")
def create_product(product: dict, response: Response):
    product["id"] = len(products) + 1
    products.append(product)
    response.status_code = 201
    return product
Enter fullscreen mode Exit fullscreen mode

La fonction @app.post() fonctionne mais comporte plusieurs lacunes. En fait, il n'y a aucune validation et aucun message d'erreur n'est retourné si cette validation ne passe pas.

Par exemple, pour créer un nouveau produit nous avons besoin du "name" et "price". Que se passe-t-il si seulement le "name" est envoyé mais pas le "price" ?

Ou bien que se passe-t-il si le "price" n'est pas un format numérique.

Je pourrais vous donner encore plusieurs exemples mais je crois que vous comprenez le concept.

Pour faire ces validations et retourner les messages d'erreurs associées, il faudrait ajouter pas mal de code à notre fonction. Et il faudrait répéter ce code pour chaque action et chaque ressource. Le tout viendrait compliquer de beaucoup notre application.

Heureusement que FastAPI porte bien son nom soit "Fast" API. Il existe donc une façon très simple d'implanter un système de validation automatique. Ce système ce sont les schémas !

Les schémas

Les schémas sont des modèles de données qui servent à FastAPI pour valider nos fonctions.

Par exemple nous pourrions définir un schéma comme celui-ci:

from pydantic import BaseModel

class Product(BaseModel):
    name: str
    price: float
Enter fullscreen mode Exit fullscreen mode

Ce modèle de donnée est très simple à comprendre. Nous avons une entité "Product" qui contient les attributs "name" et "price". Nous pouvons même définir de quel type sont les attributs.

Une fois que nous avons défini notre modèle de donné, nous pouvons modifier notre fonction @app.post()

@app.post ("/products")
def create_product(new_product: Product, response: Response):
    product = new_product.dict()
    product["id"] = len(products) + 1
    products.append(product)
    response.status_code = 201
    return product
Enter fullscreen mode Exit fullscreen mode

Nous avons remplacé le paramètre de type "dict" par un paramètre de type "Product".

Une fois cette modification faite, si vous testez cette fonction, vous verrez que FastAPI applique maintenant la validation automatique et retournera une erreur si ce que vous envoyez ne respecte pas le modèle "Product'

Put

L'action PUT permet de modifier une ressource existante. Comme l'action POST, il faut envoyer les données à modifier avec la requête.

Voici un exemple d'une fonction PUT

@app.put("/products/{id}")
def edit_product(id: int, edited_product: Product, response: Response):
    for product in products:
        if product["id"] == id:
            product['name'] = edited_product.name
            product['price'] = edited_product.price      
            response.status_code = 200
            return product
        else:
            response.status_code = 404
            return "Product Not found"
Enter fullscreen mode Exit fullscreen mode

Ici il n’y a rien de vraiment nouveau. C'est exactement les mêmes concepts que nous avons découverts un peu plus tôt.

Delete

L'action DELETE permet de supprimer une ressource. Voici le code qui permet d'implanter cette action

@app.delete("/products/{id}")
def destroy_product(id: int, response: Response):
    for product in products:
        if product["id"] == id:
            products.remove(product)
            response.status_code = 204
            return "Product Deleted"
        else:
            response.status_code = 404
            return "Product Not found"
Enter fullscreen mode Exit fullscreen mode

Base de données

Le code que vous venez de créer pour implémenter le CRUD est bien beau mais il manque tout même une notion importante et c'est de relier la ressource avec une base de données. La prochaine section va justement vous expliquer comment faire étape par étape.

Conclusion

C'est tout pour aujourd'hui, suivez-moi sur twitter : https://twitter.com/EricLeCodeur afin d'être avisé de la parution du prochain article.

💖 💪 🙅 🚩
ericlecodeur
Eric Le Codeur

Posted on November 29, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related