Hey! Aujourd’hui on aura pour mission de créer une API web en Python avec la bibliothèque Flask. Avant de rentrer dans le vive du sujet, certains se demandent peut-être « À quoi sert une API » ?
Une API (Application Programming Interface) Web est une interface qui permet à une application (ou un utilisateur) d’accéder à de la donnée grâce à une requête web.
En pratique plusieurs raisons pourraient vous pousser à mettre en place un API. Vous faites une application et vous avez besoin de données qui se trouvent dans des bases de données distantes. Il peut êtres plus simple de faire faire toutes les requêtes par une API et d’appeler cette dernière dans l’application. Cela vous offre une meilleure visibilité dans l’ensemble de votre projet et facilite le développement d’application plus complexes.
Également, faire passer les requête d’une base de données par une API peut rajouter une couche de sécurité. Vu que cela empêche l’utilisateur d’accéder directement à la base de donnée.
Un autre avantage d’une API est qu’elle est plus « light » et du coup plus rapide qu’une application web avec une interface graphique. Dans certains cas il peut être intéressant de se passer de l’interface graphique et de récupérer la donnée brute pour des soucis de délais.
Principe basique d’une API web
Une API est juste une application web qui, à chaque requête (pour un jeu de requêtes définit) renvoie de la donnée (ou un message) ou écrit de la donnée (en base).
Quelques définitions
- Méthode
Une méthode HTTP est un type d’appel, une façon d’exécuter une requête HTTP. Les méthodes les plus souvent utilisées pour les API sont les suivantes :
-
- POST
La méthode post permet d’envoyer de la donnée (Beaucoup de donnée) dans un champs spécifique qui est le « payload ». Cette méthode est souvent utilisée pour la création de ressources par l’API.
-
- GET
Cette méthode est utilisée pour récupérer de la donnée via l’API. Elle n’a pas de payload comme la méthode POST. Mais il est possible de passer des arguments dans l’URL de la requête.
-
- PUT
Cette méthode sert à modifier de la donnée déjà existante.
-
- DELETE
Vous l’aurez deviné, elle permet de supprimer de la donnée déjà existante.
Il existe d’autre méthodes HTTP cliquez pour les découvrir.
- Endpoint
Un endpoint est une URI sur laquelle on effectue une requête pour interagir avec l’API.
Exemple avec Flask
Contexte
On a une base de donnée users qui contient une table user. La table ressemble à ça :
CREATE TABLE user( email VARCHAR(120) NOT NULL, nom VARCHAR(60), prenom VARCHAR(60), ville VARCHAR(120), telephone VARCHAR(20), PRIMARY KEY (email) );
Nous allons créer des les méthodes et enpoints suivants :
- POST /api/v1/user/ : Création d’un user
- GET /api/v1/user/ : Récupération de la liste de tous les users
- GET /api/v1/user/<email> : Récupérer les informations du user correspondant à ’email’
- PUT /api/v1/user/<email>: Modifier des informations du user correspondant à ’email’
- DELETE /api/v1/user/<email> : Supprimer le user correspondant à ’email’ de la table.
Module wrapper
Je vous recommande de créer un module (un fichier wrapper.py) qui va contenir les fonctions qui interagissent avec la base de donnée.
Nous utiliserons SQLAlchemy qui est un ORM (Object Relational Mapper). Il s’agit d’une bibliothèque (Python en l’occurrence) qui fait du mapping Relationnel – Objet. C’est à dire qui permet de gérer les données relationnelles comme des objets. Ce qui est pratique, quand on code en orienté objet.
Le module ressemble à ça :
from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import create_engine, Column, String, ForeignKey MYSQL_HOST = "172.17.0.2" MYSQL_PORT = 3306 MYSQL_USER = "root" MYSQL_PWD = "secret" MYSQL_DB = "users" SQLALCHEMY_DATABASE_URI = "mysql+pymysql://{}:{}@{}:{}/{}".format(MYSQL_USER, MYSQL_PWD, MYSQL_HOST, MYSQL_PORT, MYSQL_DB) engine = create_engine(config.SQLALCHEMY_DATABASE_URI) Session = sessionmaker(bind=engine) session = Session() Base = declarative_base() class User(Base): """ Cette classe correspond à la version objet de notre table user """ __tablename__ = "user" email = Column(String(120), unique=True, nullable=False, primary_key=True) nom = Column(String(80), nullable=False) prenom = Column(String(80), nullable=False) ville = Column(String(80), nullable=False) telephone = Column(String(80), nullable=False) def add_user(email, nom, prenom, ville, telephone): try: user = User(email=email, nom=nom, prenom=prenom, ville=ville, telephone=telephone) session.add(user) session.commit() return True except Exception as e: print(e) return False def get_user_by_id(email): try: result = session.query(User).filter_by(email=email).first() return result except Exception as e: print(e) return False def get_all_users(): try: result = session.query(User).filter_by() return result except Exception as e: print(e) return False def delete_user_by_id(email): try: user_to_delete = get_user_by_id(email) if user_to_delete : session.delete(user_to_delete) session.commit() return True else: return False except Exception as e: print(e) return False def update_attribute(email, attributes): try: user_to_update = get_user_by_id(email) if user_to_update : for k,v in attributes.items(): setattr(user_to_update, k, v) session.commit() return user_to_update else: return False except Exception as e: print(e) return False
Vous avez remarqué qu’on a écrit une fonction pour chaque méthode/endpoint.
Maintenant il faut créer les routes avec Flask.
Module principal
Il s’agit de l’application en elle même. On y définit objet Flask qui represente l’application web (l’API). En suite on crée une ‘route’ (endpoint) qui n’est rien d’autre qu’une fonction décoré par le décorateur route de l’objet Flask.
import wrapper from flask import Flask, jsonify, request app = Flask(__name__) @app.route('/api/v1/user/', methods=['POST']) def create_user(): # On recupere le corps (payload) de la requete payload = request.form.to_dict() result = wrapper.add_user(**payload) if result: return jsonify(status='True', message='User created') return jsonify(status='False') @app.route('/api/v1/user/', methods=['GET']) def get_all_users(): result = wrapper.get_all_users() if result: return jsonify(status="True", result= [ {"nom":user.nom, "prenom":user.prenom, "email":user.email, "ville": user.ville, "telephone": user.telephone} for user in result.all() ]) return jsonify(status="False") @app.route('/api/v1/user/&amp;amp;amp;lt;email&amp;amp;amp;gt;', methods=['GET']) def get_user(email): result = wrapper.get_user_by_id(email) if result: return jsonify(status="True", result={"nom":result.nom, "prenom":result.prenom, "email":result.email, "ville": result.ville, "telephone": result.telephone} ) return jsonify(status="False") @app.route('/api/v1/user/&amp;amp;amp;lt;email&amp;amp;amp;gt;', methods=['PUT']) def mofify_user(email): result = wrapper.update_attribute(email, request.form.to_dict()) if result: return jsonify(status="True", message= "updated", result={ "nom":result.nom, "prenom":result.prenom, "email":result.email, "ville": result.ville, "telephone": result.telephone} ) return jsonify(status= "False") @app.route('/api/v1/user/&amp;amp;amp;lt;email&amp;amp;amp;gt;', methods=['DELETE']) def delete_user(email): result = wrapper.delete_user_by_id(email) if result: return jsonify(status="True", message= "Deleted", email=email ) return jsonify(status="False") if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True)
Pour télécharger le les fichiers : https://github.com/olivierded/ledatascientist/tree/master/api_python
N’hésitez pas à laisser des commentaires.
Et les indentations ?? Hérétique !
ok