mrkeyoor.com_
Wed 05 Aug 05:03 UTC
PyPIWeb Backendupdated 05 Aug 2026

flask

Flask is a minimal WSGI web framework for Python: routing, request and response handling, Jinja templates, signed-cookie sessions, and a dev server, with everything else (database, auth, forms, admin) left to extensions you choose yourself. It started as a wrapper around Werkzeug and Jinja and became one of the most used Python web frameworks. The 3.x line is maintained by the Pallets organization, and the core design has barely changed in a decade, which is exactly why so much existing Python web code runs on it.

Verdict

Still the fastest way to stand up a Python HTTP service and a safe, boring default for server-rendered apps and small APIs. For async-heavy or validation-heavy JSON APIs, start with FastAPI instead.

API stability5/5The core API is essentially unchanged for a decade; the 2.x and 3.x majors mostly dropped old Python versions and removed long-deprecated aliases after multi-release warnings.
Docs5/5flask.palletsprojects.com has a real tutorial, pattern guides (app factories, blueprints, deployment), and a full API reference; among the best documentation in the Python ecosystem.
Maintenance5/5Maintained by the Pallets organization with sponsor funding; last push July 2026 and only 7 open issues and PRs on a 72k-star repo, which says how settled and well-triaged it is.
Ecosystem5/548M weekly downloads and a huge extension catalog (SQLAlchemy, Login, Migrate, CORS, SocketIO); the caveat is that extensions are community-run and age at different rates.

Use it if

  • You want a small HTTP service or internal tool running in ten lines with no framework ceremony
  • You want to pick your own stack (SQLAlchemy or not, your choice of auth) instead of inheriting one
  • You are serving server-rendered HTML with Jinja templates, which ship in the box
  • You value hiring and searchability: tutorials, extensions, and Stack Overflow coverage are about as deep as Python gets
Skip it if

Setup reality

pip install flask brings Werkzeug, Jinja, click, itsdangerous, blinker, and MarkupSafe; Python 3.9+ required. The dev server behind flask run is explicitly not for production, so deployment means choosing gunicorn, waitress, or similar and wiring a reverse proxy yourself (the docs walk through it). Real apps hit the classic pains quickly: circular imports push you into the app-factory plus blueprints layout, sessions silently need a SECRET_KEY, async views need pip install flask[async], and everything beyond routing is an extension choice (Flask-SQLAlchemy, Flask-Login, Flask-Migrate) each with its own docs and release cycle.

Patterns

Minimal apphello-app

# app.py
from flask import Flask

app = Flask(__name__)

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

# run: flask run  (or: flask --app app run)

flask run auto-detects app.py or wsgi.py; anything else needs --app. The dev server is not for production.

Typed URL parameters and methodsroute-params

@app.route("/users/<int:user_id>")
def get_user(user_id):
    return {"id": user_id}

@app.post("/users")
def create_user():
    return {"created": True}, 201

Converters (int, float, path, uuid) return 404 on mismatch; app.get/app.post shortcuts exist since Flask 2.0.

Read and return JSONjson-api

from flask import request

@app.post("/items")
def create_item():
    data = request.get_json()
    return {"created": data["name"]}, 201

Returning a dict or list serializes to JSON automatically; get_json() raises 415 when Content-Type is not application/json unless you pass force=True.

Split routes into blueprintsblueprints

from flask import Blueprint

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

@bp.route("/stats")
def stats():
    return {"users": 42}

app.register_blueprint(bp)

Blueprint endpoint names are prefixed, so url_for("admin.stats"); changes made to a blueprint after registration are ignored.

Application factoryapp-factory

def create_app(test_config=None):
    app = Flask(__name__)
    app.config.from_prefixed_env()  # reads FLASK_* variables
    if test_config:
        app.config.update(test_config)

    from myapp.routes import bp
    app.register_blueprint(bp)
    return app

The factory pattern exists to dodge circular imports and to build fresh apps per test; flask run auto-detects a create_app function.

Abort and custom error responseserror-handling

from flask import abort

@app.route("/admin")
def admin():
    abort(403)

@app.errorhandler(404)
def not_found(e):
    return {"error": "not found"}, 404

An errorhandler must return the status code explicitly or the response goes out as 200.

Load configurationconfig

app.config.from_object("myapp.settings.ProdConfig")
app.config.from_prefixed_env()
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024

from_object only reads UPPERCASE attributes; from_prefixed_env parses FLASK_-prefixed variables and JSON-decodes their values.

Per-request state with g and teardownrequest-lifecycle

from flask import g, request

@app.before_request
def load_user():
    g.user = lookup_user(request.headers.get("Authorization"))

@app.teardown_appcontext
def close_db(exc):
    db = g.pop("db", None)
    if db is not None:
        db.close()

g is per-request, not shared state; code outside a request needs an explicit "with app.app_context():" or g raises a context error.

Cookie sessionssessions

import os
from flask import session

app.secret_key = os.environ["SECRET_KEY"]

@app.post("/login")
def login():
    session["user_id"] = 123
    return {"ok": True}

Sessions are signed cookies: tamper-proof but readable by the client and capped by cookie size; use a server-side session extension for sensitive or large data.

Accept file uploads safelyfile-upload

from flask import request
from werkzeug.utils import secure_filename

@app.post("/upload")
def upload():
    f = request.files["file"]
    f.save("/uploads/" + secure_filename(f.filename))
    return {"ok": True}

Set MAX_CONTENT_LENGTH or anyone can post you an arbitrarily large body; always run user filenames through secure_filename.

Async viewasync-view

@app.route("/slow")
async def slow():
    data = await fetch_remote()
    return data

Needs pip install "flask[async]"; each request still occupies a worker, so this is convenience for awaiting libraries, not FastAPI-style concurrency.

Test with the built-in clienttesting

def test_hello():
    app = create_app({"TESTING": True})
    client = app.test_client()
    res = client.get("/")
    assert res.status_code == 200

test_client() speaks WSGI directly with no server or ports involved; pair it with the app factory to get a clean app per test.

Alternatives

PackageRegistryPick it when
fastapiPyPIJSON APIs with typed validation, generated OpenAPI docs, and native async
djangoPyPIYou want the ORM, admin, auth, and migrations decided for you in one framework
quartPyPIYou like the Flask API but need ASGI, websockets, and real async