Dans FastAPI, les paramètres de requête et le corps de la requête sont des éléments essentiels pour le fonctionnement des API REST. Lorsque des données sont envoyées par le biais de ces éléments, elles doivent être validées et traitées de manière efficace. C'est là que Pydantic entre en jeu, offrant un système de validation puissant qui assure que seules des données conformes aux attentes sont transmises à l'application. Examinons comment FastAPI gère ces paramètres et comment vous pouvez en tirer parti pour créer des API robustes.

Tout d'abord, les paramètres de requête sont une méthode standard pour envoyer des données dans une URL. Avec FastAPI, ces paramètres peuvent être extraits de la requête à l'aide de la fonction Query, qui permet de spécifier des conditions comme les valeurs supérieures, inférieures ou égales à un certain seuil, ou encore de définir des valeurs par défaut. Ce processus est géré automatiquement par FastAPI, qui prend en charge la validation des données et l'analyse des paramètres en profondeur, permettant ainsi de s'assurer que les données sont correctement formatées et prêtes à être traitées.

Prenons un exemple concret : supposons que vous vouliez permettre à vos utilisateurs de filtrer des voitures en fonction de leur prix. Vous pouvez spécifier un paramètre de requête pour limiter le prix maximal des voitures renvoyées. Ce paramètre est passé via l'URL et FastAPI s'assure qu'il respecte les critères définis. En combinant les paramètres de requête et de chemin, vous pouvez créer des API flexibles et puissantes capables de traiter une grande variété de données.

Cependant, dans de nombreux cas, le corps de la requête (ou "request body") est l'endroit où la majeure partie des données est envoyée. Le corps de la requête transporte les données principales lors de la communication entre le client et l'API, qu'il s'agisse de la création de nouvelles ressources, de mises à jour ou de suppressions. Par exemple, lorsque vous utilisez une méthode POST pour créer de nouvelles ressources, vous envoyez des informations telles que les détails d'un produit, d'un utilisateur ou d'une voiture dans le corps de la requête. Ces données peuvent être envoyées au format JSON, qui est un format largement utilisé et facile à manipuler.

Dans FastAPI, il existe plusieurs façons de travailler avec le corps de la requête. Vous pouvez utiliser des modèles Pydantic pour définir la structure des données attendues dans le corps. Ces modèles agissent comme des "gardes" qui vérifient la validité des données avant qu'elles ne soient traitées par la logique métier de l'application. Par exemple, si vous voulez créer une nouvelle voiture avec des informations telles que la marque, le modèle et l'année, vous pouvez créer un modèle Pydantic qui définit ces trois champs. FastAPI se charge de valider ces champs, garantissant que seuls les bons types de données sont acceptés.

Voici un exemple simple d'utilisation d'un modèle Pydantic pour valider les données d'une nouvelle voiture :

python
from fastapi import FastAPI from pydantic import BaseModel class Car(BaseModel): brand: str model: str year: int app = FastAPI() @app.post("/cars") async def create_car(car: Car): return {"message": car}

Dans cet exemple, FastAPI utilise le modèle Car pour s'assurer que le corps de la requête contient les bons types de données : des chaînes de caractères pour la marque et le modèle, et un entier pour l'année. Si les données envoyées ne correspondent pas à cette structure, FastAPI renverra une erreur descriptive.

Un autre aspect important à comprendre est l'utilisation de Pydantic pour gérer des cas plus complexes. Si vous devez traiter des objets imbriqués, comme un utilisateur qui envoie un code promo en plus des informations de la voiture, vous pouvez créer plusieurs modèles Pydantic pour gérer ces données. FastAPI sait comment traiter plusieurs modèles dans un même point de terminaison, permettant ainsi une validation fluide des données.

Par exemple :

python
from pydantic import BaseModel class User(BaseModel): username: str name: str @app.post("/cars/user") async def create_car_with_user(car: Car, user: User, code: int = Body(None)):
return {"car": car, "user": user, "promo_code": code}

Ce code permet de traiter à la fois les données de la voiture et de l'utilisateur, avec un code promo en option. FastAPI utilise Pydantic pour valider ces informations et garantit que les données reçues respectent le format attendu.

Il est également possible de travailler avec des requêtes brutes en utilisant l'objet Request de Starlette, le framework sous-jacent de FastAPI. Bien que cette approche permette un contrôle total sur les données de la requête, elle vous prive des fonctionnalités de validation et d'auto-documentation offertes par Pydantic. Cependant, dans certaines situations, vous devrez peut-être accéder directement aux données brutes, par exemple lorsque vous avez besoin d'une manipulation très spécifique ou d'une analyse fine de la requête.

Voici un exemple de code qui montre comment accéder à une requête brute :

python
from fastapi import FastAPI, Request app = FastAPI() @app.get("/raw-request")
async def get_raw_request(request: Request):
return {"message": request.base_url, "details": dir(request)}

Dans cet exemple, la requête brute est renvoyée avec ses informations de base. Cela peut être utile dans des cas où la logique de validation et de traitement de FastAPI n'est pas nécessaire.

Il est crucial de comprendre que, bien que FastAPI vous offre des outils puissants pour gérer la validation et l'analyse des données via Pydantic, il existe des cas où vous devrez peut-être travailler directement avec des objets bruts. Dans ce cas, l'usage des fonctionnalités de base de FastAPI, comme Request, permet de manipuler les données sans passer par le système de validation automatique. Toutefois, il est recommandé d'éviter cette approche sauf si nécessaire, car elle vous prive des nombreux avantages de la validation des données et de la documentation automatique.

Comment implémenter les méthodes CRUD avec FastAPI pour gérer une collection de voitures

La gestion d’une API RESTful efficace nécessite de bien maîtriser les méthodes CRUD (Create, Read, Update, Delete). Dans cette section, nous explorerons comment mettre en œuvre ces méthodes dans FastAPI pour gérer une collection de voitures, en utilisant MongoDB comme base de données. Nous verrons également comment gérer la pagination des résultats pour éviter de saturer le client avec trop de données à la fois.

Pour commencer, imaginons que nous avons une application qui gère une collection de voitures. La première étape consiste à configurer l’endpoint pour récupérer une liste de toutes les voitures disponibles. Cet endpoint utilise la méthode GET et renvoie toutes les voitures présentes dans la base de données MongoDB.

Récupérer une voiture spécifique par son identifiant

L’implémentation d’un endpoint pour récupérer une voiture spécifique par son ID dans la collection est un besoin récurrent dans les API. Voici comment cela se fait dans FastAPI :

python
@router.get("/{id}", response_description="Get a single car by ID", response_model=CarModel)
async def show_car(id: str, request: Request): cars = request.app.db["cars"] try: id = ObjectId(id) except Exception:
raise HTTPException(status_code=404, detail=f"Car {id} not found")
if (car := await cars.find_one({"_id": ObjectId(id)})) is not None: return car raise HTTPException(status_code=404, detail=f"Car with {id} not found")

L'idée ici est simple : lorsque vous demandez une voiture par son ID, on cherche dans la collection MongoDB une entrée dont l'ID correspond. Si une voiture est trouvée, elle est retournée, sinon une erreur 404 est levée.

Mise à jour des données d'une voiture

Pour modifier les données d'une voiture, on utilise la méthode PUT. Celle-ci permet de mettre à jour les informations d’une voiture existante dans la base de données. Le code suivant illustre cette fonctionnalité :

python
async def update_car(id: str, request: Request, user=Depends(auth_handler.auth_wrapper), car: UpdateCarModel = Body(...)):
try: id = ObjectId(id) except Exception: raise HTTPException(status_code=404, detail=f"Car {id} not found") car_data = { k: v for k, v in car.model_dump(by_alias=True).items() if v is not None and k != "_id" } if len(car_data) >= 1: cars = request.app.db["cars"] update_result = await cars.find_one_and_update( {"_id": ObjectId(id)}, {"$set": car_data}, return_document=ReturnDocument.AFTER ) if update_result is not None: return update_result else:
raise HTTPException(status_code=404, detail=f"Car {id} not found")
if (existing_car := await cars.find_one({"_id": id})) is not None: return existing_car raise HTTPException(status_code=404, detail=f"Car {id} not found")

Dans ce cas, la fonction commence par s'assurer que l'ID est valide, puis elle prépare les données de la voiture à mettre à jour en excluant les champs nuls ou vides. Si des données valides sont présentes, la mise à jour est effectuée, et le document mis à jour est renvoyé.

Suppression d'une voiture

La suppression d’une voiture par son ID se fait via la méthode DELETE. Voici un exemple de son implémentation :

python
@router.delete("/{id}", response_description="Delete a car") async def delete_car(id: str, request: Request, user=Depends(auth_handler.auth_wrapper)): try: id = ObjectId(id) except Exception:
raise HTTPException(status_code=404, detail=f"Car {id} not found")
cars = request.app.db[
"cars"] delete_result = await cars.delete_one({"_id": id}) if delete_result.deleted_count == 1: return Response(status_code=status.HTTP_204_NO_CONTENT) raise HTTPException(status_code=404, detail=f"Car with {id} not found")

Dans ce cas, si la voiture existe et est supprimée avec succès, le serveur renvoie une réponse vide avec un code 204. Si la voiture n'existe pas, une erreur 404 est renvoyée.

Pagination des résultats

Lorsqu’une application génère des résultats volumineux, il est essentiel de paginer ces résultats pour éviter de surcharger le client. FastAPI permet d’implémenter facilement une pagination en utilisant les paramètres limit et skip de MongoDB. L'implémentation suivante montre comment effectuer une pagination pour récupérer les voitures par page :

python
CARS_PER_PAGE = 10
@router.get("/", response_description="List all cars, paginated", response_model=CarCollectionPagination)
async def list_cars(request: Request, page: int = 1, limit: int = CARS_PER_PAGE): cars = request.app.db["cars"] results = [] cursor = cars.find().sort("companyName").limit(limit).skip((page - 1) * limit) total_documents = await cars.count_documents({}) has_more = total_documents > limit * page async for document in cursor: results.append(document) return CarCollectionPagination(cars=results, page=page, has_more=has_more)

Ici, nous avons ajouté deux paramètres, page et limit, qui permettent de contrôler le nombre d'éléments à afficher par page et la page actuelle. La variable has_more permet de savoir s'il existe des pages supplémentaires à afficher, ce qui est essentiel pour l'interface utilisateur de pagination. Cette méthode permet de renvoyer les données de manière efficace tout en limitant la charge sur le serveur et sur le client.

Points importants à retenir

Lorsque vous concevez une API RESTful avec FastAPI et MongoDB, il est important de toujours penser à l’expérience utilisateur. Bien que les méthodes CRUD basiques (GET, POST, PUT, DELETE) soient essentielles, la gestion des erreurs, la validation des données et la pagination des résultats sont des éléments cruciaux pour créer une API robuste et performante.

La gestion des erreurs est primordiale pour garantir une expérience sans faille pour l'utilisateur final. En levant des exceptions claires et appropriées, vous permettez à l'utilisateur de comprendre ce qui ne va pas et comment y remédier.

La pagination est également un aspect fondamental lorsque vous travaillez avec de grandes quantités de données. Plutôt que d’envoyer toutes les données en une seule fois, il est plus performant de les diviser en plusieurs pages. Cette approche améliore non seulement la réactivité de l’application, mais aussi l’expérience utilisateur en lui permettant de naviguer plus facilement à travers les résultats.

Enfin, lors de l’utilisation de MongoDB, il est essentiel de comprendre le fonctionnement des requêtes asynchrones pour garantir une bonne performance et une gestion efficace des ressources du serveur.

Quelle est la structure d'une base de données MongoDB et comment gérer les documents efficacement ?

La structure d'une base de données MongoDB repose sur la flexibilité et l’adaptabilité des documents. Contrairement aux bases de données relationnelles, où les tables et les lignes sont rigides, MongoDB utilise des documents, qui sont essentiellement des paires clé-valeur organisées sous forme de structures ordonnées. Ce modèle est en réalité très proche des dictionnaires en Python ou des objets en JavaScript, ce qui en fait un choix idéal pour les applications web et de bureau modernes.

Un document dans MongoDB peut contenir des données de types variés : chaînes de caractères, entiers, booléens, objets imbriqués, tableaux, dates, données binaires, ou même des valeurs nulles. Cette variété permet de modéliser des structures complexes sans les contraintes des bases de données relationnelles. Par exemple, vous pouvez imbriquer des documents dans d'autres documents jusqu'à un niveau de 100 niveaux, ce qui donne une grande souplesse pour organiser des données hiérarchiques ou des relations complexes.

Les règles de création de documents dans MongoDB sont relativement simples : la clé doit être une chaîne de caractères et il ne doit pas y avoir de clés en doublon au sein du même document. De plus, MongoDB étant sensible à la casse, il est important de maintenir une certaine cohérence dans la manière dont les noms des clés sont écrits. Les documents sont identifiés de manière unique par un champ _id, généralement généré automatiquement par MongoDB, mais il peut être défini explicitement lors de l’insertion.

En ce qui concerne les types de données, MongoDB propose une vaste gamme d'options pour répondre aux besoins des applications modernes. Les chaînes de caractères (strings) sont l’un des types les plus utilisés, représentant des champs textuels dans les documents. Les nombres, quant à eux, sont représentés par plusieurs types différents : les entiers (int32, int64), les flottants (double) et les décimaux (decimal128) permettent une gestion fine des données numériques.

Un autre type de données crucial dans MongoDB est le tableau. Les tableaux permettent de stocker une liste de valeurs, et chaque élément du tableau peut être de n'importe quel type MongoDB, y compris un autre tableau ou un document. Ce modèle est particulièrement adapté pour représenter des relations imbriquées ou des ensembles de données liés, comme les commentaires sur un article de blog ou les membres d'une équipe.

Les objets ou documents imbriqués offrent également une flexibilité considérable, car ils permettent de structurer les données de manière plus détaillée. Par exemple, dans une application de gestion de films, un document peut inclure des informations sur le film lui-même, ses acteurs, les genres associés, les commentaires des spectateurs, etc. Chaque de ces informations peut être représentée par un champ ou un sous-document, permettant ainsi de mieux organiser et structurer les données.

Parmi les autres types de données pris en charge par MongoDB, on trouve les dates, stockées en utilisant le format BSON qui est basé sur un nombre de millisecondes depuis l’époque Unix. Ce type est essentiel pour tout système nécessitant un suivi temporel, comme dans les applications de gestion d’événements ou de transactions.

Une autre spécificité de MongoDB est l'usage des identifiants uniques, appelés ObjectId. Chaque document possède ce champ _id qui est automatiquement généré si l’on ne le spécifie pas lors de l’insertion. Un ObjectId est un identifiant de 12 octets qui garantit une unicité globale et une performance élevée dans la recherche de documents. Ce champ est particulièrement utile pour établir des relations entre documents dans des collections différentes.

Le choix du schéma et des types de données est crucial dans le processus de conception d'une application avec MongoDB. Il est important de comprendre que MongoDB, tout en offrant une grande flexibilité, ne doit pas être utilisé à tort et à travers. Par exemple, l'usage excessif de documents imbriqués ou de tableaux peut nuire à la performance, notamment en cas de lecture de documents très volumineux. De plus, bien que la structure de MongoDB soit flexible, il est souvent préférable d’utiliser des règles de validation de schéma pour garantir la cohérence des données au sein d'une même collection.

Les collections dans MongoDB, qui regroupent les documents, permettent également de bien organiser les données. Contrairement aux tables dans une base de données relationnelle, une collection MongoDB peut contenir des documents de structures très diverses, ce qui renforce l’aspect flexible du système. Cependant, pour des raisons de performance, il est souvent préférable de séparer les données en différentes collections logiques, comme par exemple une collection pour les utilisateurs, une autre pour les produits, et une dernière pour les commandes.

Enfin, bien que MongoDB puisse héberger plusieurs bases de données sur une même instance, il est recommandé de regrouper les documents utilisés par une même application dans une seule base de données. Cela optimise l'indexation et la gestion des données. De plus, MongoDB offre des fonctionnalités d’indexation avancées, permettant de rendre les requêtes plus rapides et d’améliorer la performance du système.

Lorsque vous travaillez avec MongoDB, il est essentiel de garder en tête que bien que sa structure soit flexible, une certaine rigueur dans l’organisation des données et la gestion des types est nécessaire pour garantir la performance et la facilité de gestion des informations.