Créer une API Web en Python

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;amp;lt;email&amp;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;amp;lt;email&amp;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;amp;lt;email&amp;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.

Laisser un commentaire

Votre adresse email ne sera pas publiée.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

Voulez-vous en savoir plus sur la Data Science ?

Inscrivez-vous alors à notre newsletter et vous receverez gratuitement nos derniers articles et actualités ! 
S'INSCRIRE MAINTENANT 
close-link