skip to content

flask β€” Micro Web Framework

Build lightweight web apps and REST APIs with Flask. Covers routes, request handling, JSON responses, blueprints, and the debug-server warning.

3 min read 9 snippets yesterday intermediate

flask β€” Micro Web Framework#

What it is#

Flask is a lightweight WSGI micro-framework. It gives you routing, request/response handling, and Jinja2 templates with no ORM, no admin, and no batteries forced on you. It’s the right choice for small APIs, microservices, and apps where you want full control of the stack.

Install#

pip install flask

Quick example#

# app.py
from flask import Flask, jsonify

app = Flask(__name__)

@app.get("/hello/<name>")
def hello(name: str):
    return jsonify({"message": f"Hello, {name}!"})

if __name__ == "__main__":
    app.run(debug=True)
python app.py &
curl -s http://127.0.0.1:5000/hello/Alice

Output:

{"message":"Hello, Alice!"}

When / why to use it#

  • Small REST APIs or webhooks where you don’t need Django’s full stack.
  • Prototypes you want to ship fast.
  • When you want to cherry-pick your own ORM, auth, and serialization libraries.

Prefer FastAPI when you need async, auto-generated OpenAPI docs, or Pydantic integration. Prefer Django when you need admin, migrations, and batteries included.

Common pitfalls#

[!WARNING] Never run the dev server in production β€” app.run(debug=True) is single-threaded and exposes an interactive debugger. Use gunicorn or waitress in production: gunicorn "app:app" -w 4.

[!WARNING] debug=True enables the Werkzeug debugger console β€” anyone who can reach it can execute arbitrary Python. Never use debug=True with a publicly reachable host.

[!TIP] Use flask run instead of python app.py during development. Set FLASK_DEBUG=1 in your env to enable auto-reload without hardcoding debug=True.

Richer example β€” full CRUD with blueprints#

# app.py
from flask import Flask, jsonify, request, abort, Blueprint

users_bp = Blueprint("users", __name__, url_prefix="/users")

USERS: dict[int, dict] = {
    1: {"name": "Alice"},
    2: {"name": "Bob"},
}

@users_bp.get("/")
def list_users():
    return jsonify(list(USERS.values()))

@users_bp.get("/<int:user_id>")
def get_user(user_id: int):
    user = USERS.get(user_id)
    if not user:
        abort(404, description="User not found")
    return jsonify(user)

@users_bp.post("/")
def create_user():
    data = request.get_json(force=True, silent=True) or {}
    if "name" not in data:
        abort(400, description="name is required")
    new_id = max(USERS, default=0) + 1
    USERS[new_id] = {"name": data["name"]}
    return jsonify({"id": new_id, **USERS[new_id]}), 201

@users_bp.delete("/<int:user_id>")
def delete_user(user_id: int):
    if user_id not in USERS:
        abort(404)
    del USERS[user_id]
    return "", 204

app = Flask(__name__)
app.register_blueprint(users_bp)

if __name__ == "__main__":
    app.run(debug=True)
# Start server, then:
curl -s http://127.0.0.1:5000/users/
curl -s http://127.0.0.1:5000/users/99
curl -s -X POST http://127.0.0.1:5000/users/ \
  -H "Content-Type: application/json" \
  -d '{"name": "Charlie"}'

Output:

[{"name":"Alice"},{"name":"Bob"}]
{"code":404,"description":"User not found","name":"Not Found"}
{"id":3,"name":"Charlie"}

Error handlers#

@app.errorhandler(404)
def not_found(error):
    return jsonify({"error": str(error)}), 404

@app.errorhandler(400)
def bad_request(error):
    return jsonify({"error": str(error)}), 400

Running in production#

# gunicorn (multi-process, Unix)
pip install gunicorn
gunicorn "app:app" --workers 4 --bind 0.0.0.0:8000

# waitress (cross-platform)
pip install waitress
waitress-serve --port=8000 app:app