Flask Fundamentals & Routing
When building web backends in Python, frameworks fall into two main architectural philosophies:
| Feature | Microframework (Flask / FastAPI) | Full-Stack (Django) |
|---|---|---|
| Philosophy | Minimalist & Unopinionated: Provides only routing and template rendering; you choose your own ORM, auth, and database. | "Batteries-Included": Bundles built-in ORM, admin panel, auth system, migrations, and forms out of the box. |
| Learning Curve | Gentle; start with a single 5-line Python file. | Steeper; requires understanding project/app structure and configuration settings. |
| Best Used For | Microservices, lightweight REST APIs, single-page web apps, prototyping. | Large enterprise web portals, SaaS platforms, content management systems (CMS). |
The WSGI (Web Server Gateway Interface) Lifecycle:
WSGI (PEP 3333) is the standard specification that allows Python web applications to communicate with production web servers (like Nginx, Apache, or Gunicorn).
# Minimal Single-File Flask Web Server Application:
from flask import Flask, jsonify, request
# 1. Instantiate the WSGI Application:
# __name__ informs Flask where to locate templates and static files:
app = Flask(__name__)
# 2. Define the Root Endpoint Route:
@app.route("/", methods=["GET"])
def home():
"""Returns a welcome message."""
return "๐ Welcome to Our Compiler Flask Web Server!
Status: Healthy & Online.
"
# 3. Dynamic URL Route with Integer Type Converter ():
@app.route("/users/", methods=["GET"])
def get_user_by_id(user_id):
"""Dynamic route matching /users/1, /users/42, etc."""
return jsonify({
"user_id": user_id,
"name": f"Developer #{user_id}",
"status": "Active",
"query_params_received": dict(request.args)
})
# 4. Entry point check:
if __name__ == "__main__":
# debug=True auto-reloads server when Python files are modified:
print("โก Starting local Flask development server on http://127.0.0.1:5000")
# app.run(debug=True, port=5000)
app = Flask(__name__): Creates the central WSGI application object.@app.route(...): Decorator registering the URL path pattern and allowed HTTP methods in Flask's internal URL routing table.<int:user_id>: Built-in URL converter that rejects non-integer strings (e.g./users/abcreturns 404 automatically) and casts the value to a Pythonintbefore passing it as a function argument.jsonify(...): Converts Python dictionaries into JSON responses with the appropriateContent-Type: application/jsonHTTP header.
In Flask, incoming client data is accessed through the global request context proxy:
request.args(Query Parameters): Immutable multidict containing parameters from the URL query string (e.g./search?q=python&page=2->request.args.get('q')).request.form(Form POST Data): Contains key-value pairs submitted by HTML<form method="POST">elements.request.json(REST JSON Payload): Contains deserialized JSON dictionaries submitted in POST/PUT API calls.request.headers: Dictionary containing client HTTP headers (likeUser-AgentorAuthorization).
from flask import Flask, request, jsonify
app = Flask(__name__)
# Sample in-memory catalog:
PRODUCTS_DB = [
{"id": 1, "name": "Mechanical Keyboard", "price": 2499.00},
{"id": 2, "name": "Wireless Mouse", "price": 799.00},
{"id": 3, "name": "USB-C Hub", "price": 1299.00}
]
@app.route("/api/products", methods=["GET", "POST"])
def handle_products():
if request.method == "GET":
# Extract optional max_price query filter: /api/products?max_price=1500
max_price = request.args.get("max_price", type=float)
if max_price is not None:
filtered = [p for p in PRODUCTS_DB if p["price"] <= max_price]
return jsonify(filtered)
return jsonify(PRODUCTS_DB)
elif request.method == "POST":
# Extract JSON payload from incoming POST request body:
payload = request.get_json()
if not payload or "name" not in payload or "price" not in payload:
return jsonify({"error": "Missing required fields: 'name' and 'price'"}), 400
new_item = {
"id": len(PRODUCTS_DB) + 1,
"name": payload["name"],
"price": float(payload["price"])
}
PRODUCTS_DB.append(new_item)
return jsonify({"message": "Product created successfully", "product": new_item}), 201
Always use request.args.get('param', default_val, type=int) instead of direct dictionary indexing request.args['param']. The .get() method avoids KeyError crashes if the user omits the query parameter.
Enabling debug=True starts Werkzeug's interactive in-browser debugger. In production, this allows anyone who triggers an error to execute arbitrary Python commands directly on your server terminal! Always disable debug mode and use a production WSGI server like Gunicorn in production.
Define a Flask route /calculator/
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/calculator/")
def calculate(operation):
n1 = request.args.get("num1", 0, type=float)
n2 = request.args.get("num2", 0, type=float)
if operation == "add": res = n1 + n2
elif operation == "sub": res = n1 - n2
elif operation == "mul": res = n1 * n2
elif operation == "div": res = n1 / n2 if n2 != 0 else "Error: Div by Zero"
else: return jsonify({"error": "Unknown operation"}), 400
return jsonify({"operation": operation, "num1": n1, "num2": n2, "result": res})
Q What is the difference between Flask and FastAPI?
Flask is a synchronous WSGI microframework primarily designed for HTML templating and REST APIs. FastAPI is a modern, asynchronous ASGI framework built on top of Pydantic and Starlette, providing automatic OpenAPI documentation and high-concurrency async performance.
Q What are Flask URL Converters?
URL converters match and cast dynamic parts of URLs:
Q What is the purpose of url_for() in Flask?
url_for(endpoint, **values) dynamically generates URLs based on view function names. If you change a route URL from "/user-login" to "/signin", url_for("login") updates all template links automatically without hardcoded URL breakage.