🧪 Flask Framework Overview
Flask is the framework that made a lot of people fall in love with Python on the web. It gives you just enough to start — routing, a request/response object, and templates — and then gets out of your way. This lesson explains what "microframework" really means, what lives at Flask's core, and how it stacks up against Django and FastAPI so you know exactly when to reach for it.
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Explain what a microframework is and what Flask includes versus leaves out
- Describe Flask's two core dependencies — Werkzeug and Jinja2 — and their jobs
- Trace a request through Flask's request–response cycle
- Compare Flask with Django and FastAPI and pick the right tool for a project
- Write and run a minimal Flask app and recognize the application factory pattern
Estimated Time: 30–40 minutes • Difficulty: Beginner
Hands-on: Build and run a two-route Flask app, then decide whether Flask fits a project of your own.
In This Lesson
What Is Flask?
Flask is a lightweight, flexible web framework for Python. Armin Ronacher first released it in 2010 — famously as an April Fools' joke that turned out to be genuinely useful — and it has since become one of the most widely used Python web frameworks in the world. Flask gives you the essentials for building web apps and APIs without forcing a rigid structure or a long list of dependencies on you.
💡 Flask's own tagline: "a microframework for Python based on Werkzeug, Jinja, and good intentions." The "good intentions" part is a joke, but the philosophy is real: give developers a small, dependable core and trust them to assemble the rest.
Think of Flask as a well-stocked toolbox rather than a pre-fabricated house. Django hands you a house with the plumbing, wiring, and admin office already installed — great if you want exactly that house. Flask hands you the tools and the blueprint and lets you decide which rooms to build. For small-to-medium apps, APIs, and learning, that freedom is a feature, not a burden.
What "Microframework" Means
The word micro throws people off. It does not mean Flask is only good for tiny apps, and it does not mean your whole application must fit in one file. It means Flask's core is small and deliberately unopinionated. Out of the box, Flask gives you:
- URL routing — mapping paths like
/usersto Python functions - Request and response objects — reading form data, query strings, JSON; building responses
- Template rendering — turning HTML templates into finished pages with Jinja2
- A development server and debugger — for running and troubleshooting locally
- Built-in support for unit testing — a test client that simulates requests
What Flask deliberately leaves out — and expects you to add only if you need it:
- A database layer / ORM (add Flask-SQLAlchemy)
- Form validation and CSRF protection (add Flask-WTF)
- User authentication and sessions management (add Flask-Login)
- A ready-made admin interface (add Flask-Admin)
📖 "Batteries included, but removable"
Django's motto is "batteries included" — everything ships in the box. Flask's is "batteries included, but removable." You reach for exactly the components a project needs and skip the rest, which keeps small apps small and gives you fine-grained control over how each piece behaves.
Flask's Core: Werkzeug & Jinja2
Flask itself is thin because it stands on two mature libraries from the same team (the Pallets project):
- Werkzeug — a WSGI toolkit. WSGI (Web Server Gateway Interface) is the standard that lets a Python web app talk to a web server. Werkzeug provides the request/response objects, URL routing, and the interactive debugger that Flask exposes.
- Jinja2 — a fast, expressive templating engine. It turns templates full of
{{ variables }}and{% logic %}into finished HTML.
The Request–Response Cycle
Every Flask app follows the same journey for each request. Understanding this flow makes debugging far easier, because you can pinpoint where in the chain something went wrong.
HTTP request] --> B[WSGI server
Gunicorn / dev server] B --> C[Werkzeug builds
request object] C --> D[Flask matches
URL to route] D --> E[View function
runs your code] E --> F[Jinja2 renders
a template if needed] F --> G[Response sent
back to browser]
- The browser sends an HTTP request to your server.
- A WSGI server (the built-in dev server locally, or Gunicorn/uWSGI in production) receives it.
- Werkzeug parses the raw request into a convenient
requestobject. - Flask's URL dispatcher matches the path to one of your routes.
- The matching view function runs — this is your code.
- If the view returns a template, Jinja2 renders it into HTML.
- The finished response travels back to the browser.
⚠️ The dev server is not for production
Flask's built-in server (started by flask run or app.run()) is single-threaded and unhardened. It is perfect for development but must never face real traffic. In production you run Flask behind a proper WSGI server like Gunicorn or uWSGI, usually behind Nginx.
Flask vs Django vs FastAPI
Flask is one of three Python frameworks you'll hear about constantly. None is "best" in the abstract — each optimizes for something different.
| Feature | Flask | Django | FastAPI |
|---|---|---|---|
| Style | Microframework | Full-stack "batteries included" | Modern async microframework |
| Learning curve | Low | Moderate to high | Low to moderate |
| Database / ORM | Any, via extensions | Built-in ORM | Any, via extensions |
| Async support | Partial (since 2.0) | Partial | Native (built on ASGI) |
| Best for | Small–medium apps, APIs, learning | Large apps needing structure | High-performance / typed APIs |
admin & auth built in?} B -->|Yes| C[Django] B -->|No| D{High-throughput async API
with typed validation?} D -->|Yes| E[FastAPI] D -->|No| F[Flask is a great fit]
✅ Why Flask is a great place to learn
Because Flask does little "magic," you see the wiring. You write the route, read the request, and build the response yourself — so the concepts you learn (routing, HTTP methods, templates, sessions) transfer cleanly to any framework you meet later, including Django and FastAPI.
The Extension Ecosystem
Flask's power comes from its extensions. Each is a small, focused library that plugs into the core. Here are the ones you'll meet most often:
| Extension | Adds |
|---|---|
| Flask-SQLAlchemy | Database access via the SQLAlchemy ORM |
| Flask-Migrate | Database schema migrations (built on Alembic) |
| Flask-WTF | Form handling, validation, and CSRF protection |
| Flask-Login | User sessions and authentication |
| Flask-JWT-Extended | Token-based (JWT) auth for APIs |
| Flask-Mail | Sending email |
The recommended way to wire extensions in is the application factory pattern: create each extension object once at module level, then bind it to the app inside a create_app() function with init_app(). This keeps things testable and lets you spin up differently-configured apps (development, testing, production) from the same code.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
# Create extension objects once, unbound to any app yet
db = SQLAlchemy()
migrate = Migrate()
login_manager = LoginManager()
def create_app(config_object="config.DevelopmentConfig"):
app = Flask(__name__)
app.config.from_object(config_object)
# Bind the extensions to THIS app instance
db.init_app(app)
migrate.init_app(app, db)
login_manager.init_app(app)
login_manager.login_view = "auth.login"
# Register blueprints (route groups)
from app.main import bp as main_bp
app.register_blueprint(main_bp)
return app
💡 Why the factory pattern?
Defining the app inside a function (instead of as a global) means your tests can create a fresh, isolated app with a testing config, and you avoid the circular-import headaches that plague single-file apps as they grow. We build this out fully in the next lesson.
Your First Flask App
Enough theory — here is a complete, runnable Flask application. Save it as app.py:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return "Hello, Flask!"
@app.route("/greet/<name>")
def greet(name):
return f"Hello, {name}!"
if __name__ == "__main__":
app.run(debug=True)
Then, from a terminal in the same folder (with Flask installed in your virtual environment), run it. Either style works:
# Recommended: the Flask CLI
flask --app app run --debug
# Or run the file directly
python app.py
Visit http://127.0.0.1:5000/ and you'll see:
Hello, Flask!
And http://127.0.0.1:5000/greet/Ray responds with Hello, Ray! — the <name> part of the URL is captured and passed straight into your function.
That's the whole idea in miniature: a route maps a URL to a function, and the function's return value becomes the response. In the lessons ahead we replace those plain strings with rendered templates, JSON, and database-backed data — but the shape stays exactly this simple.
Hands-on Exercise
🏋️ Build a Two-Route "About Me" App
Objective: Get a real Flask app running and prove you understand routing.
Instructions:
- Create a virtual environment and install Flask:
python -m venv venv, activate it, thenpip install flask. - Create
app.pywith two routes:/that returns a welcome message, and/aboutthat returns a sentence about you. - Add a third, dynamic route
/hello/<name>that greets whatever name appears in the URL. - Run it with
flask --app app run --debugand visit all three URLs in your browser. - In one sentence, write down why Flask (rather than Django) is a reasonable choice for an app this size.
💡 Hint
Each route is a function directly beneath its @app.route(...) decorator. For the dynamic route, put <name> in the path and add name as the function's parameter — the two must match.
✅ Sample solution
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return "Welcome to my first Flask app!"
@app.route("/about")
def about():
return "I'm Ray, learning full stack web development."
@app.route("/hello/<name>")
def hello(name):
return f"Hello, {name}! Nice to meet you."
if __name__ == "__main__":
app.run(debug=True)
Why Flask here: the app is tiny and needs no admin panel, ORM, or auth — Flask's small core delivers exactly what's needed with zero overhead.
🎯 Quick Quiz
Question 1: What does calling Flask a "microframework" mean?
Question 2: Which two libraries form Flask's core?
Question 3: When would FastAPI be a better pick than Flask?
Summary & Quiz
🎉 Key Takeaways
- Flask is a lightweight, flexible Python microframework — small core, add what you need.
- "Microframework" means minimal core, not minimal capability; extensions cover ORM, forms, auth, and more.
- Flask stands on Werkzeug (WSGI toolkit) and Jinja2 (templates).
- Every request follows the same request–response cycle; the dev server is for development only.
- Choose Flask for small–medium apps, APIs, and learning; Django for big structured sites; FastAPI for typed async APIs.
- The application factory pattern (
create_app()+init_app()) is the modern, testable way to structure a Flask app.
📚 Further Reading
- Flask Official Documentation
- Flask Tutorial (build a small blog)
- Flask — Application Factories
- Werkzeug Documentation
🚀 What's Next?
Next we'll set up a real Flask project from scratch — virtual environment, folder structure, configuration classes, and the full application factory — so you have a solid foundation to build every later feature on.
🎉 Nice work!
You know what Flask is, what makes it tick, and when to reach for it. Time to build a proper project scaffold.