Créer une API rapidement avec Flask

illustration de l'article

Dans le cadre d’un projet, j’ai eu besoin de développer une API rapidement. Je me suis alors posée la question de l’outil à utiliser. En faisant quelques recherches, je suis tombée plusieurs fois sur Flask. Ayant déjà fait un peu de python, je me suis lancée dans la découverte de ce framework…​

A la découverte de Flask

La première version de Flask date de 2010. Il s’agit d’un framework de développement web en python. Ce framework open-source est qualifié de micro-framework car il est très léger. Il est particulièrement adapté dans le cas de petites applications ou de PoC (Proof of Concept), ce qui correspond tout à fait à notre cas d’usage.

Flask est utilisé par de nombreux développeurs. On peut retrouver son utilisation dans des sites tels que LinkedIn ou encore Pinterest.

Pourquoi utiliser Flask ?

Flask permet une prise en main simple et rapide, tout en permettant d’évoluer vers des applications plus complexes. En 5 lignes seulement, il est possible de créer une application web simplifiée.

hello world code => hello world on browser

Pré-requis : installation de python

Commencez par vérifier que python est bien installé sur votre ordinateur.

$ python3 --version

Si ce n’est pas le cas, installez-le :

$ sudo apt install python3

Pour installer python sur d’autres OS que Linux : https://www.python.org/downloads/

Initier un nouveau projet

Création du dossier

Créons un dossier dans lequel mettre notre nouveau projet. Appelons le bookshop-api :

$ mkdir bookshop-api

Versioning du code

Afin de versionner notre api, nous allons initialiser un nouveau dépôt git :

$ cd bookshop-api
$ git init

N’oublions pas de créer un fichier .gitignore qui comportera les fichiers/dossiers qu’on ne veut pas versionner ni partager. Nous le remplirons par la suite.

$ touch .gitignore

Mettre en place un environnement virtuel

Il est recommandé de créer un environnement virtuel pour chaque projet sur lequel on travaille, dans lequel on installera localement les packages dont nous avons besoin. En effet, cela permet de travailler sur différents projets sans se soucier de la compatibilité entre les versions des librairies des différents projets.

Pour créer un nouvel environnement virtuel, lancer à la racine du projet :

$ cd bookshop-api
$ python3 -m venv <name_of_virtual_env>

Appelons venv notre environnement virtuel. Maintenant que nous l’avons créé, il faut l’activer :

$ source venv/bin/activate

L’environnement virtuel ne doit pas être versionné/partagé. Pour cela nous ajoutons la ligne suivante dans le fichier .gitigore :

/venv/

Installation de Flask

Entrons maintenant dans le vif du sujet : l’installation de Flask.

Pour installer Flask dans l’environnement virtuel :

$ pip install flask
$ python -c "import flask; print(flask.__version__)"
$ pip install -U flask-cors
$ pip install python-dotenv

L’installation du package flask-cors permet la gestion du CORS. Pour en savoir plus sur ce package, rendez-vous ici. Pour en savoir plus sur le CORS, ou "Cross-origin resource sharing", rendez-vous ici.

Nous verrons plus bas l’utilisation du package python-dotenv.

Création d’un premier endpoint

Créer un fichier main.py à la racine du projet, qui sera le point de départ de notre API.

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello World!'

Comme vous pouvez le constater, il ne suffit vraiment que de 5 lignes pour créer un premier endpoint.

Lancement de l’application

Variables d’environnement

Définissons le nom de l’application ainsi que l’environnement de développement. La variable FLASK_APP permet d’indiquer à Flask où trouver l’application. Ici, le nom de l’application est main car le fichier à lancer est main.py. La variable FLASK_ENV indique quant à elle à Flask dans quel mode exécuter l’application. Ici, nous voulons l’exécuter en mode developpement, afin d’activer automatiquement le mode de debug. La valeur par défaut étant FLASK_ENV=production.

Nous allons enregistrer ces variables d’environnement dans un fichier .env situé à la racine du projet :

FLASK_APP=main
FLASK_ENV=development

N’oubliez pas d’ajouter ce fichier .env dans votre .gitignore. Les variables d’environnement ne sont pas supposées être partagées, surtout dans le cas où il s’agit de variables secrètes.

Le package python-dotenv est nécessaire pour que ce fichier contenant les variables d’environnement soit automatiquement lu au lancement de l’application. C’est pourquoi nous l’avons installé lors de la mise en place de l’application.

Lancement de l’API

Pour démarrer le serveur Flask, lancer la commande suivante :

$ flask run

La console affiche alors les lignes suivantes :

 * Serving Flask app "main" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 813-894-335

En se rendant à l’adresse http://127.0.0.1:5000/, la phrase "Hello World" apparaît sur la page du navigateur.

Ajout de nouvelles routes

Ce n’est pas tout d’afficher un "Hello World", passons maintenant aux choses sérieuses : la création des routes de notre librairie en ligne.

CRUD(S) endpoints

Dans le cadre de ce projet, j’avais besoin de répondre aux besoins suivants :

  • Récupérer la liste de tous les livres ;

  • Récupérer un livre par son id ;

  • Créer un livre ;

  • Modifier un livre ;

  • Supprimer un livre.

Nous allons donc créer 5 endpoints correspondant aux 5 opérations CREATE, READ, UPDATE, DELETE, et SEARCH dans le fichier main.py:

from flask import Flask, request, jsonify
from flask_cors import CORS
from utils.seeds import default_books
from domain.Book import Book, from_json

app = Flask(__name__)
CORS(app, supports_credentials=True)

@app.route('/')
def hello():
    return 'Hello World!'

@app.route('/books', methods=['GET'])
def get_all_books():
    return jsonify([book.to_json() for book in default_books]), 200

@app.route('/books/<book_id>', methods=['GET'])
def get_one_book(book_id):
        for book in default_books:
        if str(book.id) == str(book_id):
            return book.to_json(), 200
    return jsonify({'error': 'book not found'}), 404

@app.route('/books', methods=['POST'])
def create_book():
    data = request.get_json()
    book = from_json(data)
    book.id = len(default_books) + 1
    default_books.append(book)
    return jsonify({'message': 'book successfully created'}), 200

@app.route('/books/<book_id>', methods=['PUT'])
def update_book(book_id):
    data = request.get_json()
    updated_book = from_json(data)
    for book in default_books:
        if str(book.id) == str(book_id):
            index = default_books.index(book)
            default_books[index] = updated_book
            return jsonify({'message': "book successfully updated"}), 200
    return jsonify({'error': 'book not found'}), 404

@app.route('/books/<book_id>', methods=['DELETE'])
def delete_book(book_id):
    for book in default_books:
        if str(book.id) == str(book_id):
            default_books.remove(book)
            return jsonify({'message': 'book successfully deleted'}), 200
    return jsonify({'error': 'book not found'}), 404

La classe Book se trouve dans le fichier Book.py :

import string

class Book:
    def __init__(self, title: string, code_name: string, author: string, quantity: int, publish_date: string,
                id: int = 0) -> None:
        self.id = id
        self.title = title
        self.code_name = code_name
        self.author = author
        self.quantity = quantity
        self.publish_date = publish_date

    def to_json(self):
        return {
            'id': self.id,
            'title': self.title,
            'codeName': self.code_name,
            'author': self.author,
            'publishDate': self.publish_date,
            'quantity': self.quantity
        }

def from_json(data):
    return Book(
        data['title'], data['codeName'], data['author'], data['quantity'], data['publishDate'], data['id']
    )

La liste de livres default_books se trouve dans le fichier seeds.py

from domain.Book import Book

b1 = Book("Juste un regard", "juste_un_regard", "Harlan Coben", 8, 2010, 1)
b2 = Book("Ne le dis à personne", "ne_le_dis_a_personne", "Harlan Coben", 1, 2005, 2)
b3 = Book("Dans les bois", "dans_les_bois", "Harlan Coben", 5, 2020, 3)
b4 = Book("Balle de match", "balle_de_match", "Harlan Coben", 3, 2007, 4)
b5 = Book("Disparu à jamais", "disparu_a_jamais", "Harlan Coben", 3, 2003, 5)
b6 = Book("Promets moi", "promets_moi", "Harlan Coben", 7, 1999, 6)

default_books: [Book] = [b1, b2, b3, b4, b5, b6]

Appels à l’API

Méthode GET - Récupérer tous les livres

Requête curl :

curl http://127.0.0.1:5000/books

Résultat :

[
  {
    "author": "Harlan Coben",
    "codeName": "innocent",
    "id": 1,
    "publishDate": 2005,
    "quantity": 10,
    "title": "Innocent"
  },
  {
    "author": "Harlan Coben",
    "codeName": "ne_le_dis_a_personne",
    "id": 2,
    "publishDate": 2001,
    "quantity": 7,
    "title": "Ne le dis à personne"
  },
  {
    "author": "Harlan Coben",
    "codeName": "dans_les_bois",
    "id": 3,
    "publishDate": 2007,
    "quantity": 12,
    "title": "Dans les bois"
  },
  {
    "author": "Harlan Coben",
    "codeName": "balle_de_match",
    "id": 4,
    "publishDate": 2006,
    "quantity": 3,
    "title": "Balle de match"
  }
]

Méthode GET - Récupérer un livre

Requête curl :

curl http://127.0.0.1:5000/books/1

Résultat :

{
  "author": "Harlan Coben",
  "codeName": "innocent",
  "id": 1,
  "publishDate": 2005,
  "quantity": 10,
  "title": "Innocent"
}

Méthode POST - Créer un livre

Requête curl :

curl -X POST http://127.0.0.1:5000/books
-H "Content-Type: application/json"
-d '{"author": "Harlan Coben", "title": "Juste un regard", "publishDate": 2004, \
"quantity": 12, "codeName": "juste_un_regard", "id": ""}'

Résultat :

{
  "message": "book successfully created"
}

Méthode PUT - Editer un livre

Requête curl :

curl -X PUT http://127.0.0.1:5000/books/5
-H "Content-Type: application/json"
-d '{"author": "Harlan Coben", "title": "Juste un regard", "publishDate": 2004, "quantity": 20, \
"codeName": "juste_un_regard", "id": 5}'

Résultat :

{
  "message": "book successfully updated"
}

Méthode DELETE - Supprimer un livre

Requête curl :

curl -X DELETE http://127.0.0.1:5000/books/46

Résultat :

{
  "message": "book successfully deleted"
}

Livre non trouvé

Requête curl :

curl http://127.0.0.1:5000/books/157

Résultat :

{
  "error": "book not found"
}

Conclusion

Le choix de Flask était adapté pour mon cas d’usage. En effet, la prise en main a été très facile, et une API simplifiée a pu être créée en quelques lignes.

Python est un langage très utilisé, et cela vaut le coup de s’y mettre pour utiliser Flask. En effet, la montée en compétence est très rapide.

Flask étant un micro-framework, il faut tout développer soi-même tandis qu’avec un framework plus complet tel que Django, le développement de certaines fonctionnalité peut être facilité. De plus, la communauté de Flask n’est pas aussi grande que celle de Django.

Cependant, il faut garder à l’esprit que l’utilisation de Flask est surtout recommandée pour de petites applications, pour lesquelles Flask est le choix idéal.

Pour aller plus loin :

  • Flask permet de renvoyer des templates HTML directement. Ayant déjà un front et n’ayant que besoin d’une API, ce sujet n’a pas été abordé ici mais vous pourrez trouver de la documentation à ce sujet sur le site DigitalOcean.

  • La connexion avec une base de données n’a pas été traitée dans cet article, mais elle peut également se faire aisément avec Flask.

Date

Auteur

Avatar Pauline Maitre

Pauline Maitre

Développeuse Web

Catégories

back

Tags

#api #python #flask