Il est crucial de protéger les informations sensibles, notamment les données de carte de crédit, lors de leur stockage dans une base de données. Un système efficace de gestion de la sécurité peut être mis en place en utilisant FastAPI, SQLAlchemy, et des techniques de cryptage appropriées. En particulier, il est recommandé de chiffrer les informations sensibles avant de les enregistrer dans la base de données et de les décrypter uniquement lorsque cela est nécessaire. Une telle approche garantit que même en cas de violation de sécurité, les données restent protégées.

Dans un environnement de production, il est essentiel de définir où la clé de chiffrement, ou cypher_key, sera stockée. Elle peut être soit conservée dans un service externe de gestion des clés, offrant des fonctionnalités telles que la rotation des clés, soit générée au démarrage de l’application, selon les besoins spécifiques de sécurité de l’entreprise.

Afin d’implémenter cette sécurité, il est possible de créer des fonctions qui chiffrent et déchiffrent les informations de carte de crédit, comme le montre l'exemple suivant :

python
def encrypt_credit_card_info(card_info: str) -> str: return cypher_suite.encrypt(card_info.encode()).decode()
def decrypt_credit_card_info(encrypted_card_info: str) -> str:
return cypher_suite.decrypt(encrypted_card_info.encode()).decode()

Ces fonctions seront utilisées lors de l’écriture et de la lecture des informations dans la base de données.

Ensuite, pour stocker les informations sensibles de manière sécurisée, une fonction telle que celle-ci peut être définie dans le module security.py :

python
async def store_credit_card_info( db_session: AsyncSession, card_number: str, card_holder_name: str, expiration_date: str, cvv: str, ): encrypted_card_number = encrypt_credit_card_info(card_number) encrypted_cvv = encrypt_credit_card_info(cvv) credit_card = CreditCard( number=encrypted_card_number, card_holder_name=card_holder_name, expiration_date=expiration_date, cvv=encrypted_cvv, ) async with db_session.begin(): db_session.add(credit_card) await db_session.flush() credit_card_id = credit_card.id await db_session.commit() return credit_card_id

Chaque fois que cette fonction est appelée, les informations de carte de crédit sont stockées sous forme chiffrée, garantissant que les données sensibles ne sont jamais enregistrées en clair dans la base de données.

De manière similaire, une fonction peut être définie pour récupérer ces informations chiffrées à partir de la base de données. Voici un exemple de code pour cette opération :

python
async def retrieve_credit_card_info(
db_session: AsyncSession, credit_card_id: int ): query = select(CreditCard).where(CreditCard.id == credit_card_id) async with db_session as session: result = await session.execute(query) credit_card = result.scalars().first() credit_card_number = decrypt_credit_card_info(credit_card.number) cvv = decrypt_credit_card_info(credit_card.cvv) card_holder = credit_card.card_holder_name expiry = credit_card.expiration_date return { "card_number": credit_card_number, "card_holder_name": card_holder, "expiration_date": expiry, "cvv": cvv, }

Dans ce cas, les informations de carte de crédit sont extraites et décryptées à partir de la base de données, permettant d’accéder aux données sensibles uniquement lorsqu’elles sont nécessaires.

Il est également important d’assurer la robustesse du système en ajoutant des tests unitaires pour valider les opérations de chiffrement. Par exemple, un test pourrait vérifier que les informations de carte de crédit sont correctement chiffrées avant d’être stockées et que les données sont sécurisées lorsqu’elles sont enregistrées dans la base de données.

L’utilisation d’un algorithme de chiffrement symétrique, comme Fernet, est recommandée pour garantir la sécurité des données. Cette approche peut être approfondie en consultant la documentation officielle de Fernet pour obtenir des informations détaillées sur la façon de gérer la cryptographie dans les applications Python.


Il est important de comprendre qu’en plus de chiffrer les données sensibles, la gestion des transactions et de la concurrence est tout aussi cruciale dans un environnement de base de données. Lorsqu’il y a plusieurs transactions simultanées, il est nécessaire de mettre en place des mécanismes de contrôle de la concurrence, comme les verrous, pour éviter que des transactions ne se chevauchent et ne provoquent des incohérences ou des corruptions de données. Les transactions garantissent également que les modifications apportées à la base de données sont atomiques, ce qui est essentiel pour maintenir l’intégrité des données.

Un autre aspect essentiel à comprendre est la gestion des opérations concurrentes dans un environnement avec plusieurs utilisateurs accédant à des ressources partagées. Par exemple, dans un système de billetterie, plusieurs utilisateurs peuvent essayer d'acheter le même billet simultanément. Pour gérer cette concurrence, une approche de verrouillage optimiste, où une transaction peut échouer si la ressource a été modifiée entre-temps, peut être mise en œuvre. Cela permet d’assurer que chaque transaction est validée avant d’apporter des modifications permanentes à la base de données, en évitant toute collision entre les opérations concurrentes.

Comment organiser vos endpoints et utiliser les routeurs avec FastAPI

Dans FastAPI, l'organisation du code en utilisant des routeurs et des endpoints constitue une pratique fondamentale pour la gestion de votre projet. Cette approche permet de maintenir un code propre, modulaire et évolutif. Les endpoints représentent les points d'interaction de l'API, tandis que les routeurs servent à organiser ces endpoints en groupes logiques.

Prenons d'abord l'exemple d'un endpoint basique. Un endpoint dans FastAPI est créé en décorant une fonction avec une méthode HTTP, telle que @app.get("/"). Cela signifie qu'un appel GET à l'URL racine de votre application invoquera la fonction correspondante, retournant une réponse JSON. Voici un extrait de code simple qui montre comment définir cet endpoint :

python
from fastapi import FastAPI
app = FastAPI() @app.get("/") async def read_root(): return {"Hello": "World"}

Dans cet exemple, un appel GET à l'URL racine (/) invoque la fonction read_root(), qui retourne une réponse JSON contenant un message de bienvenue. Ce type d'endpoint est très simple, mais il est la base sur laquelle des fonctionnalités plus complexes peuvent être construites.

Pour une application de taille plus importante, où plusieurs endpoints sont nécessaires, il devient rapidement pertinent d'utiliser des routeurs. Les routeurs permettent de regrouper des endpoints dans des modules séparés, ce qui facilite la gestion et la lecture du code. Par exemple, on peut utiliser un routeur pour gérer toutes les opérations liées aux utilisateurs, et un autre pour les opérations liées aux produits.

Supposons que nous souhaitions créer un routeur qui gère les éléments d'un catalogue, comme des articles. Voici comment cela pourrait être mis en place :

python
from fastapi import APIRouter router = APIRouter() @router.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}

Ce code définit un endpoint qui accepte un paramètre de chemin item_id et retourne l'identifiant de l'élément demandé. Ensuite, pour que ce routeur soit utilisé dans votre application FastAPI principale, vous devez l'inclure dans le fichier main.py de la manière suivante :

python
import router_example
from fastapi import FastAPI app = FastAPI() app.include_router(router_example.router) @app.get("/") async def read_root(): return {"Hello": "World"}

De cette manière, vous centralisez la logique liée aux éléments dans un fichier séparé, facilitant la maintenance et la lisibilité de votre code. FastAPI gère de manière transparente l'inclusion des routeurs dans l'instance de l'application.

Une fois cette configuration en place, il vous suffit de démarrer votre serveur FastAPI avec Uvicorn. Pour ce faire, dans le répertoire de votre projet, lancez la commande suivante :

bash
$ uvicorn main:app --reload

L'option --reload permet au serveur de se redémarrer automatiquement chaque fois que des modifications sont apportées au code, ce qui est très utile lors du développement. Une fois le serveur en cours d'exécution, vous pouvez accéder à votre API via l'adresse http://127.0.0.1:8000.

L'un des avantages majeurs de FastAPI est la génération automatique de documentation interactive. Dès que votre application est en marche, deux interfaces de documentation sont créées automatiquement : Swagger UI et Redoc. Vous pouvez accéder à ces interfaces via les URLs suivantes :

  • Swagger UI : http://127.0.0.1:8000/docs

  • Redoc : http://127.0.0.1:8000/redoc

Ces interfaces permettent de tester facilement les endpoints de votre API sans écrire de code supplémentaire, offrant ainsi une manière intuitive de découvrir les fonctionnalités de votre API et de vérifier son bon fonctionnement. Vous pouvez même exécuter des requêtes API directement depuis ces interfaces et visualiser les réponses en temps réel.

Un autre aspect important à comprendre dans FastAPI est la gestion des paramètres, qu'ils soient de type path (dans l'URL) ou query (dans la chaîne de requête). Par exemple, dans un endpoint tel que /books/{book_id}, book_id est un paramètre de chemin dynamique, et FastAPI l'extraira automatiquement pour le transmettre à la fonction associée. Cette gestion automatique des paramètres est l'une des raisons pour lesquelles FastAPI est si populaire parmi les développeurs, car elle simplifie la validation et l'extraction des données de la requête.

Voici un exemple de création d'un endpoint qui prend un paramètre de chemin et retourne des informations sur un livre :

python
from fastapi import FastAPI app = FastAPI() @app.get("/books/{book_id}")
async def read_book(book_id: int):
return {"book_id": book_id, "title": "The Great Gatsby", "author": "F. Scott Fitzgerald"}

Dans cet exemple, lorsque vous appelez l'endpoint /books/{book_id}, FastAPI valide automatiquement le paramètre book_id en tant qu'entier. Si un autre type de donnée est fourni, une réponse d'erreur appropriée est renvoyée.

Outre l'utilisation des endpoints et des routeurs, il est essentiel de maîtriser la gestion des types de données dans FastAPI. L'utilisation d'annotations de type permet non seulement de valider les entrées, mais également d'optimiser les performances de votre API en facilitant la documentation automatique. Cela fait partie de l'approche orientée vers les types que FastAPI adopte, rendant le processus de développement plus fluide et moins sujet aux erreurs.

Une fois votre serveur en cours d'exécution, vous pouvez tester vos endpoints avec des outils tels que Swagger UI, qui est intégré dans FastAPI, ou utiliser des clients API plus robustes comme Postman. Ces outils vous permettent de simuler des requêtes HTTP et de vérifier les réponses, ce qui est essentiel pour tester la fonctionnalité de vos endpoints avant leur déploiement en production.

Pour une application réelle, il est crucial de bien comprendre comment utiliser les paramètres path et query. Les paramètres path permettent de capturer des valeurs directement dans l'URL, tandis que les paramètres query sont passés dans la chaîne de requête. Ces deux types de paramètres jouent un rôle clé dans la flexibilité de votre API, et leur maîtrise est nécessaire pour construire des APIs puissantes et adaptées aux besoins des utilisateurs.

Enfin, bien que les routeurs facilitent l'organisation du code, il est également important de respecter les principes de conception d'une API RESTful, notamment en définissant clairement les méthodes HTTP associées à chaque endpoint et en suivant les bonnes pratiques pour la gestion des erreurs et des réponses. Une API bien structurée garantit une meilleure évolutivité et une expérience utilisateur plus fluide.