Python Framework

Flask

Flask est le micro-framework Python minimaliste. Vous disposez du routage, d'un objet requête et des templates Jinja dans un seul fichier, puis vous ajoutez précisément les extensions dont vous avez besoin à mesure que l'application grandit.

beginner14 min readUpdated 16 sept. 2026
app.py
python
# app.py
from flask import Flask, jsonify

app = Flask(__name__)


@app.get("/health")
def health():
    return jsonify(status="ok")


if __name__ == "__main__":
    app.run(debug=True)
Sortie
2010
Langage
Python
Style
Minimaliste, non-opinionné
Idée centrale
Un seul objet app
Templates
Jinja2
Version actuelle
3.x

Pourquoi c'est important

Pourquoi Flask est un excellent premier framework

Un seul fichier pour commencer

Importez Flask, créez une app, ajoutez un décorateur et lancez. Il n'y a pas de générateur de projet ni de structure de dossiers imposée à apprendre au préalable.

Vous choisissez vos composants

Le cœur est volontairement restreint. La base de données, l'authentification, les formulaires et les migrations proviennent d'extensions bien maintenues que vous choisissez d'intégrer.

Du Python pur partout

Les routes sont des fonctions, la configuration est un dictionnaire et la requête est un objet lisible. Rien n'est caché derrière une API de framework massive.

Le tableau complet

App, route, blueprint

Un objet app, des décorateurs qui mappent les URL vers des fonctions, et des blueprints pour grouper les routes quand le projet s'agrandit.

L'objet app

Initialiser

Flask(__name__) crée l'application. Il détient la configuration, enregistre les routes et constitue l'objet appelé par le serveur WSGI.

Les routes

Mapper

Le décorateur @app.route lie une règle d'URL à une fonction. Des convertisseurs capturent des valeurs typées comme des entiers ou des chemins.

Les Blueprints

Composer

Un Blueprint groupe des routes, des templates et des fichiers statiques dans un module qui peut être enregistré sur l'app, souvent plusieurs fois.

HTML5 en un coup d'oeil

La boîte à outils Flask

Routage par décorateur

@app.get("/posts/<int:id>") lie un motif d'URL à une fonction de vue.

Requête et réponse

Lisez request.args, request.form et request.get_json(); retournez des chaînes, des dicts ou des objets Response.

Templates Jinja2

render_template remplit le HTML avec des données et échappe les variables par défaut.

Blueprints

Divisez une application volumineuse en modules avec des préfixes et leurs propres templates.

Extensions

SQLAlchemy, Login, Migrate et consorts ajoutent les fonctionnalités absentes du cœur.

Client de test

app.test_client() effectue des requêtes en interne sans qu'aucun serveur ne soit lancé.

Un bref aperçu

D'une blague du poisson d'avril à un standard du web

  1. 2010

    Une blague devient un framework

    Armin Ronacher publie Flask, basé sur Werkzeug et Jinja2, après qu'un projet de poisson d'avril a suscité un réel intérêt.

    10
  2. 2011

    L'écosystème d'extensions

    Flask-SQLAlchemy et d'autres extensions établissent le modèle « cœur léger, composants optionnels ».

    11
  3. 2018

    Flask 1.0

    Après des années de versions 0.x, Flask s'engage sur une API publique stable.

    18
  4. 2021

    Flask 2.0

    L'arrivée des vues asynchrones et des décorateurs courts comme @app.get modernise l'expérience développeur.

    21
  5. 2023

    Flask 3.0

    Un cœur plus léger supprime les API obsolètes depuis longtemps et resserre le contexte de requête.

    23
  6. Aujourd'hui

    Toujours la référence minimaliste

    Flask reste le choix privilégié pour les petits services, les prototypes et l'enseignement des fondamentaux du web.

    Aujourd'hui

Le guide complet

Flask: Tout ce que vous devez savoir

Qu’est-ce que Flask ?

Flask est un framework web minimaliste pour Python. Il vous fournit un objet d’application, un moyen de mapper des URLs à des fonctions, ainsi qu’un modèle de requête et de réponse. Tout le reste — bases de données, authentification, formulaires, tâches de fond — est une décision que vous prendrez plus tard, généralement en ajoutant une extension.

Cette légèreté est tout l’intérêt du framework. Là où Django a une opinion tranchée sur la manière dont un projet doit être structuré, Flask n’en a pratiquement aucune. Pour une petite API, un outil interne ou un premier projet web, cette liberté est un véritable atout. Vous pouvez parcourir l’intégralité du framework en un après-midi et comprendre chaque ligne de votre propre application.

Flask repose sur deux bibliothèques historiques : Werkzeug pour la plomberie WSGI et Jinja2 pour les templates. Toutes deux sont matures et bien documentées, et savoir qu’elles sont au cœur du système permet d’expliquer la majeure partie du comportement de Flask.

L’application Flask la plus petite

Une application Flask complète peut tenir en quelques lignes.

# app.py
from flask import Flask

app = Flask(__name__)


@app.get("/")
def index():
    return "Hello, world"


if __name__ == "__main__":
    app.run(debug=True)

Flask(__name__) crée l’application et lui indique où trouver les templates et les fichiers statiques. Le décorateur lie une URL à la fonction située en dessous. app.run() lance le serveur de développement. La condition if __name__ == "__main__" empêche le serveur de démarrer lorsque le module est importé par un test ou un serveur WSGI.

Le routage avec les décorateurs

Le décorateur @app.route est le cœur de Flask. Il permet d’enregistrer une règle d’URL ainsi que la fonction qui y répond.

@app.route("/posts")
def list_posts():
    return "All posts"


@app.route("/posts/<int:post_id>")
def get_post(post_id):
    return f"Post {post_id}"

Les règles peuvent contenir des parties variables écrites entre chevrons. Flask fait correspondre l’URL, convertit la valeur et la transmet à la fonction sous forme d’argument nommé (keyword argument). Si aucune règle ne correspond, Flask renvoie automatiquement une erreur 404.

Variables d’URL et méthodes HTTP

Les convertisseurs rendent les variables typées avant l’exécution de votre code. Les plus courants sont string (par défaut), int, float, path (qui autorise les slashs) et uuid.

@app.get("/users/<username>")
def profile(username):
    return f"Profile for {username}"


@app.get("/files/<path:filepath>")
def serve_file(filepath):
    return f"File at {filepath}"

Flask 2.0 a ajouté des raccourcis pour chaque méthode. @app.get, @app.post, @app.put, @app.patch et @app.delete sont plus explicites que de passer une liste methods, et une seule fonction peut répondre à plusieurs méthodes.

@app.route("/posts", methods=["GET", "POST"])
def posts():
    if request.method == "POST":
        return create_post()
    return list_posts()

L’objet request

Les données entrantes se trouvent dans l’objet global request, que Flask alimente pour la requête actuelle. Il s’agit d’un proxy, vous pouvez donc l’importer une seule fois en haut du module sans risque.

from flask import request

@app.get("/search")
def search():
    query = request.args.get("q", "")
    page = request.args.get("page", 1, type=int)
    return {"query": query, "page": page}


@app.post("/posts")
def create_post():
    payload = request.get_json()
    title = payload["title"]
    return {"title": title}, 201

request.args contient les valeurs de la query-string, request.form contient les données des formulaires (posts), request.get_json() analyse le corps JSON, request.files contient les fichiers téléchargés, et request.headers fournit les headers bruts. Comme il s’agit de mappings multi-valeurs, utilisez .get() et définissez des valeurs par défaut plutôt que d’indexer les données aveuglément.

Réponses et JSON

Une vue peut retourner une chaîne de caractères, un dictionnaire (que Flask sérialise en JSON), un tuple de (body, status) ou (body, status, headers), ou un objet Response complet.

from flask import jsonify, make_response

@app.post("/posts")
def create_post():
    post = {"id": 1, "title": "Hello"}
    return jsonify(post), 201


@app.get("/ping")
def ping():
    response = make_response({"pong": True})
    response.headers["Cache-Control"] = "no-store"
    return response

jsonify est la méthode explicite pour retourner du JSON : elle sérialise les données et définit le type de contenu. Retourner un dictionnaire fonctionne également, mais jsonify est plus clair quant à l’intention et gère davantage de types.

Templates avec Jinja2

render_template charge un fichier depuis le dossier templates/ et le rend avec les données que vous transmettez. Jinja2 échappe les variables par défaut, ce qui protège contre les XSS.

from flask import render_template

@app.get("/posts/<int:post_id>")
def get_post(post_id):
    post = {"id": post_id, "title": "Hello", "body": "First post"}
    return render_template("post.html", post=post)
<!-- templates/post.html -->
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>{{ post.title }}</title>
  </head>
  <body>
    <h1>{{ post.title }}</h1>
    <p>{{ post.body }}</p>
  </body>
</html>

Jinja supporte {% extends %} pour l’héritage, {% include %} pour les fragments, {% for %} et {% if %} pour la logique, ainsi que des filtres tels que {{ value|upper }}. Gardez vos templates purement présentations ; si la logique devient trop complexe, déplacez-la dans la vue.

Fichiers statiques

Par défaut, Flask sert tout le contenu du dossier static/ via /static/.... Référencez-le avec url_for, ce qui permet de construire l’URL à partir du nom du point de terminaison.

<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" />

En production, laissez un reverse proxy ou un CDN servir les fichiers statiques plutôt que le processus Python. C’est plus rapide et cela libère l’application pour traiter les requêtes.

Blueprints

Un Blueprint est une collection de routes et de templates pouvant être enregistrés sur une application. C’est ainsi qu’une application Flask composée d’un seul module peut évoluer vers un projet complet sans devenir un chaos.

# blog/views.py
from flask import Blueprint, jsonify

bp = Blueprint("blog", __name__, url_prefix="/blog")


@bp.get("/")
def index():
    return jsonify(posts=[])


@bp.get("/<int:post_id>")
def detail(post_id):
    return jsonify(id=post_id)
# app.py
from blog.views import bp as blog_bp

app.register_blueprint(blog_bp)

Les Blueprints possèdent leur propre préfixe d’URL, leurs propres templates et fichiers statiques, et un seul blueprint peut être enregistré plusieurs fois avec des préfixes différents. Ils facilitent également les tests et la revue de code car chaque fonctionnalité est autonome.

La fabrique d’application (Application Factory)

Une fois que vous utilisez des blueprints, l’étape suivante logique est la mise en place d’une fonction fabrique (factory function) pour construire l’application. C’est la structure standard pour tout projet dépassant le stade de la simple démo.

# app.py
from flask import Flask

from blog.views import bp as blog_bp


def create_app(config_object="config.Config"):
    app = Flask(__name__)
    app.config.from_object(config_object)

    app.register_blueprint(blog_bp)

    return app
# wsgi.py
from app import create_app

app = create_app()

La fabrique rend les tests triviaux : vous construisez une application avec une configuration de test, vous exécutez le client, puis vous la supprimez. Cela permet également d’éviter l’importation d’une application globale partiellement configurée, ce qui est à l’origine de la plupart des problèmes d’importations circulaires dans Flask.

Extensions

Le cœur du framework étant minimal, c’est l’écosystème qui fournit le reste. Quelques extensions couvrent la majorité des besoins des projets :

  • Flask-SQLAlchemy intègre l’ORM de SQLAlchemy et gère la session par requête.
  • Flask-Migrate encapsule Alembic pour permettre le versionnage des changements de schéma.
  • Flask-Login gère les sessions utilisateur et le proxy current_user.
  • Flask-WTF ajoute la gestion des formulaires, la protection CSRF et l’upload de fichiers.
  • Flask-CORS contrôle les requêtes cross-origin pour les API.
# extensions.py
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

db = SQLAlchemy()
migrate = Migrate()
# app.py
from extensions import db, migrate


def create_app():
    app = Flask(__name__)
    app.config.from_object("config.Config")

    db.init_app(app)
    migrate.init_app(app, db)

    return app

Le pattern init_app permet de créer une extension sans application et de la lier plus tard, ce qui permet de garder la factory propre et l’extension importable par les modèles.

Configuration

La configuration est un simple mapping sur app.config. Chargez-la depuis une classe, un fichier ou l’environnement, et gardez vos secrets hors du contrôle de version.

# config.py
import os


class Config:
    SECRET_KEY = os.environ["SECRET_KEY"]
    SQLALCHEMY_DATABASE_URI = os.environ["DATABASE_URL"]
    JSON_SORT_KEYS = False
app.config.from_object("config.Config")
app.config.from_prefixed_env()

from_prefixed_env() lit les variables d’environnement de FLASK_*, ce qui est pratique dans les conteneurs. Utilisez différentes classes ou fichiers par environnement, et ne commitez jamais le vrai SECRET_KEY — il sert à signer les sessions et les jetons CSRF.

Gestion des erreurs

Enregistrez des gestionnaires d’erreurs pour renvoyer des réponses cohérentes dans toute l’application. Les gestionnaires peuvent être globaux ou limités à un blueprint.

from flask import jsonify
from werkzeug.exceptions import HTTPException


@app.errorhandler(404)
def not_found(error):
    return jsonify(error="not_found"), 404


@app.errorhandler(Exception)
def unhandled(error):
    if isinstance(error, HTTPException):
        return error
    app.logger.exception(error)
    return jsonify(error="internal_error"), 500

Le gestionnaire pour HTTPException est important : les erreurs intégrées de Flask sont également des exceptions, et un gestionnaire Exception générique transformerait autrement une erreur 404 en erreur 500.

Tests

Le client de test de Flask effectue des requêtes au sein du même processus, sans serveur ni réseau.

import pytest

from app import create_app


@pytest.fixture
def client():
    app = create_app("config.TestConfig")
    with app.test_client() as client:
        yield client


def test_health(client):
    response = client.get("/health")
    assert response.status_code == 200
    assert response.get_json() == {"status": "ok"}


def test_missing_post(client):
    response = client.get("/posts/999")
    assert response.status_code == 404

La factory et test_client() fonctionnent ensemble : chaque test reçoit une instance d’application fraîche, ce qui empêche les fuites d’état entre les tests. Surchargez la configuration pour les tests et utilisez une base de données en mémoire lorsque vous en ajouterez une.

WSGI et déploiement

Flask est une application WSGI, ce qui signifie que n’importe quel serveur WSGI peut l’exécuter. Le serveur de développement est réservé exclusivement au développement.

pip install gunicorn
gunicorn --workers 4 --bind 0.0.0.0:8000 "app:create_app()"

En production, utilisez Gunicorn ou uWSGI derrière Nginx, gérez la terminaison TLS au niveau du proxy, et servez les fichiers statiques via le proxy ou un CDN. Configurez DEBUG=False, mettez en place la journalisation (logging) et récupérez vos secrets depuis les variables d’environnement. La conteneurisation de l’application rend le déploiement portable d’un hôte à l’autre.

Bonnes pratiques

  • Commencez par un module unique, puis passez à une factory et des blueprints à mesure que le projet s’agrandit.
  • Utilisez url_for pour construire vos URLs au lieu de coder les chemins en dur.
  • Gardez la configuration dans des classes ou des variables d’environnement, jamais directement dans le code.
  • Utilisez jsonify et des codes de statut explicites pour les réponses API.
  • Validez chaque donnée d’entrée avant qu’elle n’atteigne votre logique métier.
  • Enregistrez des gestionnaires d’erreurs pour que les échecs retournent un format cohérent.
  • Utilisez le client de test et une instance d’application fraîche pour chaque test.
  • Ne lancez jamais le serveur de développement en production.

Erreurs courantes

  • Laisser debug=True activé en production, ce qui expose un débogueur interactif.
  • Importer un app global partout, créant ainsi des imports circulaires.
  • Lire request.json sans gérer les corps de requête malformés ou manquants.
  • Écrire les URL en dur dans les templates au lieu d’utiliser url_for.
  • Construire un app.py monolithique avec des centaines de routes.
  • Oublier la protection CSRF lors de l’utilisation de formulaires.
  • Stocker des secrets dans config.py et les commiter.
  • Supposer que Flask possède un ORM ou un système d’authentification intégré, pour ensuite tenter de recréer les deux maladroitement.

Et après ?

Flask est le moyen le plus rapide de comprendre le fonctionnement réel d’un framework web, car très peu de choses sont masquées. Si vous recherchez un ORM, des migrations, l’authentification et une interface d’administration sans avoir à tout assembler vous-même, consultez le guide Django. Si vous voulez une validation automatique et une documentation API basée sur les type hints, passez à FastAPI. Pour concevoir les endpoints que vous venez de créer, le guide sur les REST APIs est le compagnon idéal. Si vous ajoutez une base de données, la roadmap backend couvre SQL, le langage qui sous-tend l’ORM.

Retourner du JSON

jsonify sérialise les données Python, définit le type de contenu application/json et gère les codes de statut. json.dumps vous laisse construire toute la réponse vous-même.

Préférer
from flask import jsonify

@app.get("/posts/<int:id>")
def get_post(id):
    return jsonify(id=id, title="Hello")
Éviter
import json
from flask import Response

@app.get("/posts/<int:id>")
def get_post(id):
    body = json.dumps({"id": id, "title": "Hello"})
    return Response(body, mimetype="application/json")

Création de l'app

Une fonction factory permet aux tests de créer une app fraîche avec une configuration de test, tandis qu'une app au niveau du module est créée une seule fois à l'importation et est difficile à reconfigurer.

Préférer
def create_app(config=None):
    app = Flask(__name__)
    app.config.from_object(config or "config.Config")
    register_blueprints(app)
    return app
Éviter
app = Flask(__name__)
app.config["SECRET_KEY"] = "dev"

# imported everywhere; hard to test in isolation

Compromis

Flask est-il le bon choix par défaut ?

Flask privilégie la liberté et une surface d'attaque réduite. C'est libérateur pour les petites apps, mais cela peut devenir un fardeau quand l'app grandit.

Strengths

  • Vous comprenez tout le système

    Il y a très peu de magie. Chaque route, valeur de config et extension est visible, ce qui fait de Flask un excellent framework pour apprendre le fonctionnement du web.

  • Minimaliste et flexible

    Vous choisissez la base de données, la bibliothèque de validation et la stratégie d'auth au lieu d'hériter de décisions que vous n'avez pas prises.

  • Parfait pour les petits services

    Un microservice JSON ou un outil interne tient souvent dans un seul fichier, et rien dans le framework ne s'y oppose.

Trade-offs

  • Vous assemblez votre propre stack

    L'auth, les migrations, les formulaires et l'admin sont votre responsabilité. Chaque extension a ses propres patterns et sa propre courbe d'apprentissage.

  • Les conventions sont à vous d'inventer

    Rien n'impose de structure de projet, donc deux apps Flask peuvent être totalement différentes. Les grandes équipes doivent définir leur propre guide de style.

  • Moins de fonctionnalités natives

    Les headers de sécurité, le comportement de l'ORM et la validation sont optionnels. Vous devez penser à les ajouter plutôt que d'être protégé par défaut.

FAQ

Foire aux questions

Keep learning

Related topics from the roadmap.

$ commencer à apprendre

Prêt à apprendre Flask ?

Notre tutoriel interactif vous guide à travers Flask pas à pas — avec des quiz et du vrai code que vous pouvez exécuter dans le navigateur.