Python Framework

Flask

Flask ist das minimale Python-Micro-Framework. Routing, ein Request-Objekt und Jinja-Templates erhältst du in einer einzigen Datei; weitere Erweiterungen fügst du genau dann hinzu, wenn die App wächst.

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)
Veröffentlicht
2010
Sprache
Python
Stil
Minimal, unopinionated
Kernidee
Ein App-Objekt
Templates
Jinja2
Aktuelle Version
3.x

Warum es wichtig ist

Warum Flask ein großartiges erstes Framework ist

Start mit einer Datei

Flask importieren, eine App erstellen, einen Decorator hinzufügen und starten. Es gibt keinen Projekt-Generator und kein erforderliches Ordner-Layout, das man zuerst lernen muss.

Du wählst die Bausteine

Der Kern ist bewusst klein gehalten. Datenbank, Auth, Formulare und Migrationen stammen aus gut gepflegten Extensions, für die du dich entscheidest.

Durchgehend Plain Python

Routen sind Funktionen, die Konfiguration ist ein Dictionary und der Request ist ein auslesbares Objekt. Nichts versteckt sich hinter einer riesigen Framework-API.

Das Gesamtbild

App, Route, Blueprint

Ein App-Objekt, Decorators, die URLs auf Funktionen mappen, und Blueprints, die Routen bei wachsenden Projekten gruppieren.

Das App-Objekt

Initialisierung

Flask(__name__) erstellt die Anwendung. Es hält die Konfiguration, registriert Routen und ist das Objekt, das der WSGI-Server aufruft.

Routen

Matching

Der @app.route Decorator bindet eine URL-Regel an eine Funktion. Converter erfassen typisierte Werte wie Integer und Pfade.

Blueprints

Komposition

Ein Blueprint gruppiert Routen, Templates und statische Dateien in einem Modul, das in der App registriert werden kann – oft sogar mehrfach.

HTML5 auf einen Blick

Die Flask-Toolbox

Decorator-Routing

@app.get("/posts/<int:id>") bindet ein URL-Pattern an eine View-Funktion.

Request und Response

Lies request.args, request.form und request.get_json(); gib Strings, Dicts oder Response-Objekte zurück.

Jinja2-Templates

render_template füllt HTML mit Daten und escaped Variablen standardmäßig.

Blueprints

Teile eine große App in Module mit Präfixen und eigenen Templates auf.

Extensions

SQLAlchemy, Login, Migrate und andere fügen die Teile hinzu, die im Kern fehlen.

Test-Client

app.test_client() führt Requests in-process aus, ohne dass ein Server laufen muss.

Eine kurze Geschichte

Vom April-Scherz zum Web-Standard

  1. 2010

    Ein Scherz wird zum Framework

    Armin Ronacher veröffentlicht Flask, basierend auf Werkzeug und Jinja2, nachdem ein April-Scherz-Projekt echtes Interesse weckte.

    10
  2. 2011

    Das Extension-Ökosystem

    Flask-SQLAlchemy und andere Extensions etablieren das Modell "kleiner Kern, optionale Bausteine".

    11
  3. 2018

    Flask 1.0

    Nach Jahren von 0.x-Releases verpflichtet sich Flask zu einer stabilen öffentlichen API.

    18
  4. 2021

    Flask 2.0

    Async-Views und kurze Decorators wie @app.get erscheinen und modernisieren die Developer Experience.

    21
  5. 2023

    Flask 3.0

    Ein schlankerer Kern entfernt lange veraltete APIs und strafft den Request-Kontext.

    23
  6. Heute

    Immer noch der kleine Standard

    Flask bleibt die erste Wahl für kleine Services, Prototypen und das Lehren von Web-Grundlagen.

    Heute

Der vollständige Leitfaden

Flask: Alles was Sie wissen müssen

Was ist Flask?

Flask ist ein minimales Web-Framework für Python. Es bietet dir ein Application-Objekt, eine Möglichkeit, URLs auf Funktionen zu mappen sowie ein Request- und Response-Modell. Alles andere – Datenbanken, Authentifizierung, Formulare, Background-Jobs – ist eine Entscheidung, die du später triffst, meist durch das Hinzufügen einer Extension.

Genau diese Schlankheit ist der entscheidende Punkt. Django hat eine feste Meinung dazu, wie ein Projekt strukturiert sein sollte; Flask hat fast keine. Für eine kleine API, ein internes Tool oder das erste Web-Projekt ist diese Freiheit ein Feature. Du kannst das gesamte Framework an einem Nachmittag lesen und jede einzelne Zeile deiner eigenen App verstehen.

Flask basiert auf zwei bewährten Bibliotheken: Werkzeug für das WSGI-Plumbing und Jinja2 für Templates. Beide sind ausgereift und gut dokumentiert. Wenn man weiß, dass sie im Hintergrund laufen, lässt sich das meiste Verhalten von Flask leicht erklären.

Die kleinste Flask-App

Eine vollständige Flask-Anwendung passt in nur wenige Zeilen.

# 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__) erstellt die Anwendung und gibt an, wo Templates und statische Dateien zu finden sind. Der Decorator bindet eine URL an die darunterliegende Funktion. app.run() startet den Development-Server. Der if __name__ == "__main__"-Guard verhindert, dass der Server gestartet wird, wenn das Modul von einem Test oder einem WSGI-Server importiert wird.

Routing mit Decorators

Der @app.route Decorator ist das Herzstück von Flask. Er registriert eine URL-Regel und die Funktion, die darauf antwortet.

@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}"

Regeln können variable Teile enthalten, die in spitzen Klammern geschrieben werden. Flask matcht die URL, konvertiert den Wert und übergibt ihn als Keyword-Argument an die Funktion. Wenn keine Regel matcht, gibt Flask automatisch einen 404-Fehler zurück.

URL-Variablen und HTTP-Methoden

Converter machen Variablen typsicher, bevor Ihr Code ausgeführt wird. Die gängigsten sind string (der Standard), int, float, path (welcher Slashes erlaubt) und 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 hat Shortcuts für jede Methode hinzugefügt. @app.get, @app.post, @app.put, @app.patch und @app.delete sind übersichtlicher, als eine methods-Liste zu übergeben, und eine einzige Funktion kann auf mehrere Methoden antworten.

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

Das Request-Objekt

Eingehende Daten befinden sich im globalen request-Objekt, das Flask für den aktuellen Request befüllt. Da es sich um einen Proxy handelt, ist es sicher, dieses einmalig am Anfang des Moduls zu importieren.

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 enthält Query-String-Werte, request.form enthält Formular-Posts, request.get_json() parst einen JSON-Body, request.files enthält Uploads und request.headers liefert die rohen Header. Da es sich hierbei um Multi-Value-Mappings handelt, sollten Sie .get() verwenden und Standardwerte angeben, anstatt blind über Indizes zuzugreifen.

Antworten und JSON

Ein View kann einen String, ein Dictionary (das Flask in JSON serialisiert), ein Tuple aus (body, status) oder (body, status, headers) oder ein vollständiges Response-Objekt zurückgeben.

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 ist der explizite Weg, um JSON zurückzugeben: Es serialisiert die Daten und setzt den Content-Type. Die Rückgabe eines Dictionarys funktioniert ebenfalls, aber jsonify macht die Absicht deutlicher und unterstützt mehr Datentypen.

Templates mit Jinja2

render_template lädt eine Datei aus dem templates/-Ordner und rendert sie mit den übergebenen Daten. Jinja2 escaped Variablen standardmäßig, was vor XSS schützt.

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 unterstützt {% extends %} für Vererbung, {% include %} für Fragmente, {% for %} und {% if %} für die Logik sowie Filter wie {{ value|upper }}. Halten Sie Templates rein präsentationsorientiert; wenn die Logik zu komplex wird, verschieben Sie diese in den View.

Statische Dateien

Standardmäßig stellt Flask alles im Ordner static/ unter /static/... bereit. Referenziere diesen mit url_for, wodurch die URL aus dem Namen des Endpunkts generiert wird.

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

Überlasse in der Production-Umgebung das Ausliefern statischer Dateien einem Reverse Proxy oder einem CDN anstelle des Python-Prozesses. Das ist performanter und entlastet die App, sodass sie sich auf die Verarbeitung von Requests konzentrieren kann.

Blueprints

Ein Blueprint ist eine Sammlung von Routes und Templates, die in einer App registriert werden können. So wächst eine Flask-App aus einem einzelnen Modul zu einem Projekt heran, ohne im Chaos zu enden.

# 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)

Blueprints besitzen ihr eigenes URL-Präfix, eigene Templates und statische Dateien. Zudem kann ein Blueprint mehrfach mit unterschiedlichen Präfixen registriert werden. Sie erleichtern zudem das Testen und den Code-Review, da jedes Feature in sich abgeschlossen ist.

Die Application Factory

Sobald Sie Blueprints verwenden, ist der nächste logische Schritt eine Factory-Funktion, die die App aufbaut. Dies ist die Standardstruktur für alles, was über eine Demo hinausgeht.

# 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()

Die Factory macht Tests trivial: Erstellen Sie eine App mit einer Test-Konfiguration, führen Sie den Client aus und verwerfen Sie die Instanz anschließend wieder. Zudem wird verhindert, dass eine halb konfigurierte globale App importiert wird – was die Hauptursache für die meisten Circular-Import-Probleme in Flask ist.

Extensions

Da der Kern minimal gehalten ist, wird der Rest durch das Ökosystem bereitgestellt. Ein paar Extensions decken die meisten Projekte ab:

  • Flask-SQLAlchemy integriert das ORM von SQLAlchemy und verwaltet die Session pro Request.
  • Flask-Migrate kapselt Alembic, sodass Schema-Änderungen versioniert werden.
  • Flask-Login verwaltet User-Sessions und den current_user Proxy.
  • Flask-WTF fügt Formulare, CSRF-Schutz und Datei-Uploads hinzu.
  • Flask-CORS steuert Cross-Origin-Requests für APIs.
# 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

Das init_app Pattern ermöglicht es, eine Extension ohne App zu erstellen und sie später zu binden. Das hält die Factory sauber und macht die Extension für Models importierbar.

Konfiguration

Die Konfiguration ist ein einfaches Mapping auf app.config. Laden Sie diese aus einer Klasse, einer Datei oder der Umgebung und halten Sie Secrets aus der Versionsverwaltung fern.

# 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() liest FLASK_* Umgebungsvariablen aus, was in Containern sehr praktisch ist. Verwenden Sie pro Umgebung unterschiedliche Klassen oder Dateien und committen Sie niemals den echten SECRET_KEY — dieser signiert Sessions und CSRF-Tokens.

Fehlerbehandlung

Registrieren Sie Error Handler, um app-weit konsistente Antworten zurückzugeben. Handler können global definiert oder auf einen Blueprint beschränkt werden.

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

Der Handler für HTTPException ist besonders wichtig: Die integrierten Fehler von Flask sind ebenfalls Exceptions. Ein allgemeiner Exception-Handler würde daher einen 404-Fehler in einen 500-Fehler verwandeln.

Testing

Der Test-Client von Flask führt Anfragen in-process aus, ohne dass ein Server oder ein Netzwerk benötigt wird.

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

Die Factory und test_client() arbeiten zusammen: Jeder Test erhält eine frische App, sodass keine Zustände zwischen den Tests übertragen werden können. Überschreibe die Konfiguration für Tests und verwende eine In-Memory-Datenbank, sobald du eine hinzufügst.

WSGI und Deployment

Flask ist eine WSGI-Anwendung, was bedeutet, dass jeder WSGI-Server sie ausführen kann. Der Development-Server ist ausschließlich für die Entwicklung gedacht.

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

Nutzen Sie in der Produktion Gunicorn oder uWSGI hinter Nginx, terminieren Sie TLS am Proxy und stellen Sie statische Dateien über den Proxy oder ein CDN bereit. Setzen Sie DEBUG=False, konfigurieren Sie das Logging und lesen Sie Secrets aus der Umgebung aus. Die Containerisierung der App macht das Deployment über verschiedene Hosts hinweg portabel.

Best Practices

  • Beginnen Sie mit einem einzelnen Modul und wechseln Sie zu einer Factory und Blueprints, sobald die Anwendung wächst.
  • Verwenden Sie url_for zum Erstellen von URLs, anstatt Pfade hart zu kodieren.
  • Lagern Sie Konfigurationen in Klassen oder Umgebungsvariablen aus, niemals direkt im Code.
  • Nutzen Sie jsonify und explizite Status-Codes für API-Antworten.
  • Validieren Sie jede Eingabe, bevor sie Ihre Geschäftslogik erreicht.
  • Registrieren Sie Error-Handler, damit Fehler in einem konsistenten Format zurückgegeben werden.
  • Verwenden Sie den Test-Client und eine frische App-Instanz pro Test.
  • Lassen Sie den Development-Server niemals in der Production-Umgebung laufen.

Häufige Fehler

  • debug=True in der Production aktiviert zu lassen, wodurch ein interaktiver Debugger exponiert wird.
  • Einen globalen app überall zu importieren und dadurch zirkuläre Imports zu erzeugen.
  • request.json zu lesen, ohne fehlerhafte oder fehlende Bodys zu behandeln.
  • URLs in Templates hart zu codieren, anstatt url_for zu verwenden.
  • Eine monolithische app.py mit hunderten von Routes aufzubauen.
  • Die CSRF-Protection bei der Verwendung von Formularen zu vergessen.
  • Secrets in config.py zu speichern und diese zu committen.
  • Davon auszugehen, dass Flask ein integriertes ORM oder Auth-System besitzt, und dann beides schlecht selbst zu implementieren.

Wie geht es weiter?

Flask ist der schnellste Weg, um zu verstehen, was ein Web-Framework eigentlich macht, da kaum etwas im Hintergrund verborgen bleibt. Wenn du ein ORM, Migrationen, Auth und ein Admin-Panel suchst, ohne diese selbst zusammenstellen zu müssen, lies den Django-Guide. Wenn du automatische Validierung und API-Dokumentationen basierend auf Type Hints möchtest, wechsle zu FastAPI. Um die Endpunkte, die du gerade erstellt hast, professionell zu gestalten, ist der REST APIs-Guide die ideale Ergänzung. Falls du eine Datenbank hinzufügst, deckt die Backend-Roadmap SQL ab – die Sprache, die unter dem ORM liegt.

JSON zurückgeben

jsonify serialisiert Python-Daten, setzt den application/json Content-Type und handhabt Status-Codes. json.dumps überlässt es dir, die gesamte Response selbst zu bauen.

Bevorzugt
from flask import jsonify

@app.get("/posts/<int:id>")
def get_post(id):
    return jsonify(id=id, title="Hello")
Vermeiden
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")

Die App erstellen

Eine Factory-Funktion erlaubt es Tests, eine frische App mit Test-Konfiguration zu bauen, während eine App auf Modulebene nur einmal beim Import erstellt wird und schwer neu zu konfigurieren ist.

Bevorzugt
def create_app(config=None):
    app = Flask(__name__)
    app.config.from_object(config or "config.Config")
    register_blueprints(app)
    return app
Vermeiden
app = Flask(__name__)
app.config["SECRET_KEY"] = "dev"

# imported everywhere; hard to test in isolation

Abwägungen

Ist Flask der richtige Standard?

Flask optimiert auf Freiheit und eine kleine Angriffsfläche. Das ist befreiend für kleine Apps, kann aber zur Last werden, sobald die App wächst.

Strengths

  • Du verstehst das gesamte System

    Es gibt sehr wenig Magie. Jede Route, jeder Konfigurationswert und jede Extension ist sichtbar, was Flask zu einem exzellenten Framework macht, um zu lernen, wie das Web funktioniert.

  • Minimal und flexibel

    Du wählst die Datenbank, die Validierungs-Library und die Auth-Strategie selbst, anstatt Entscheidungen zu übernehmen, die du nicht getroffen hast.

  • Perfekt für kleine Services

    Ein JSON-Microservice oder ein internes Tool besteht oft aus einer einzigen Datei, und nichts im Framework steht dem im Weg.

Trade-offs

  • Du stellst den Stack zusammen

    Auth, Migrationen, Formulare und Admin liegen in deiner Verantwortung. Jede Extension hat eigene Patterns und eine eigene Lernkurve.

  • Konventionen musst du selbst erfinden

    Nichts erzwingt ein Projekt-Layout, sodass zwei Flask-Apps völlig unterschiedlich aussehen können. Große Teams benötigen einen eigenen House-Style.

  • Weniger Out-of-the-box

    Security-Header, ORM-Verhalten und Validierung sind optional. Du musst daran denken, sie hinzuzufügen, anstatt standardmäßig geschützt zu sein.

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, Flask zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch Flask — mit Quizzen und echtem Code, den Sie im Browser ausführen können.