Cloud Tech by Victor
BackendIntermediate

Python Web Frameworks Overview

How Flask, Django, and FastAPI trade minimalism, batteries-included structure, and async-first design differently, and how to actually choose based on project shape.

Updated 2026-07-223 min read

Overview

Flask, Django, and FastAPI solve the same basic problem, routing HTTP requests to Python code, with genuinely different philosophies. Flask is a minimal microframework that leaves most decisions (ORM, auth, structure) to the developer; Django is a batteries-included framework with strong conventions and a full built-in toolkit; FastAPI is async-first and type-hint-driven, generating request validation and API documentation directly from function signatures. None of the three is a strict upgrade over another, the right choice depends on project shape: how much structure is wanted out of the box, whether async I/O concurrency actually matters for the workload, and how the team prefers to work.

Quick Reference

FrameworkPhilosophyProtocolBest fit
FlaskMinimal, unopinionatedWSGI (sync)Small services, full control over structure
DjangoBatteries-included, conventionsWSGI (+ASGI support)Content-heavy apps needing admin/ORM/auth out of the box
FastAPIAsync-first, type-hint-drivenASGI (async)High-concurrency APIs, auto-generated docs/validation

Syntax

python
# Flask - minimal, explicit
from flask import Flask
app = Flask(__name__)

@app.route("/health")
def health():
    return {"status": "ok"}
python
# FastAPI - type hints drive validation and OpenAPI docs
from fastapi import FastAPI
app = FastAPI()

@app.get("/users/{user_id}")
async def get_user(user_id: int) -> dict:
    return {"id": user_id}

Examples

python
# Django - conventions and a built-in ORM come with the framework
from django.db import models

class Order(models.Model):
    total_cents = models.IntegerField()
    created_at = models.DateTimeField(auto_now_add=True)

Async syntax alone doesn't make a framework fast at concurrency

Using async def inside a WSGI (synchronous) framework doesn't grant real I/O concurrency the way an ASGI framework does, the underlying server model still processes one request per worker at a time. The protocol, not just the function syntax, determines whether concurrency benefits are real.

Visual Diagram

Common Mistakes

  • Choosing FastAPI for its async support without an actual I/O-concurrency need, taking on an async-first mental model for no real benefit.
  • Fighting Django's conventions instead of using them, effectively rebuilding a Flask-like unopinionated structure inside a framework designed around strong defaults.
  • Using Flask for a large, content-heavy application and hand-building an ORM/admin/auth stack that Django would have provided out of the box.
  • Assuming async def route handlers automatically make a WSGI-based framework concurrent, the underlying server protocol, not the function keyword, determines that.

Performance

  • FastAPI/ASGI frameworks can handle significantly more concurrent connections per worker for I/O-bound workloads than WSGI frameworks, because of the same cooperative-scheduling model asyncio uses generally.
  • Django's ORM and built-in features add some overhead compared to a minimal Flask app, but that overhead is rarely the actual bottleneck in a real application, database query patterns dominate in practice.
  • Raw framework choice matters far less to real-world performance than application-level decisions: query patterns, caching, and I/O-bound vs. CPU-bound workload shape.

Best Practices

  • Choose based on project shape and team preference, not framework popularity, a batteries-included framework isn't automatically better for a project that needs a handful of simple endpoints.
  • Reach for FastAPI specifically when high I/O concurrency or auto-generated request validation/docs are real requirements, not just because it's newer.
  • Use Django's conventions rather than working against them once it's chosen; its value is largely in the integrated toolkit and shared structure.
  • Confirm whether async actually helps the workload (I/O-bound, high concurrency) before choosing an async-first framework for that reason alone.

Interview questions

What is the core philosophical difference between Flask and Django?

Flask is a microframework; it provides routing and request/response handling and deliberately leaves everything else (ORM, admin panel, authentication, forms) to be chosen and added by the developer. Django is batteries-included; it ships with an ORM, an admin interface, an authentication system, and a forms library as an integrated whole, with strong conventions about how a project should be structured. Flask trades built-in structure for flexibility; Django trades flexibility for a consistent, fully-equipped starting point. Neither is strictly better, the right choice depends on whether a project benefits more from Django's conventions or needs the freedom to pick its own pieces.

What does FastAPI provide that Flask does not, and what's the trade-off?

FastAPI is async-first (built on ASGI, not WSGI) and uses Python type hints to automatically generate request validation, serialization, and interactive OpenAPI documentation, a type-annotated function signature becomes both the API contract and its enforcement, with no separate schema to maintain by hand. The trade-off is that FastAPI assumes an async-first mental model and a type-hint-driven style throughout, which is a bigger shift for a team used to Flask's simpler, synchronous, un-opinionated style, and FastAPI has a smaller, younger ecosystem of plugins/extensions compared to Flask or Django's much longer history.

Why does the choice between WSGI and ASGI matter when picking a Python web framework?

WSGI (Web Server Gateway Interface) is the traditional, synchronous interface between Python web applications and servers, one request is handled by one worker thread/process at a time, blocking for its duration. ASGI (Asynchronous Server Gateway Interface) extends that to support async request handling and other async protocols (WebSockets), letting a single worker handle many concurrent requests while they're waiting on I/O, the same underlying model as asyncio generally. Flask and Django historically are WSGI (Django has gained ASGI support); FastAPI is ASGI-native. The choice matters because it determines whether the framework can actually benefit from async I/O concurrency, or whether it's fundamentally a one-request-per-worker model regardless of async syntax used inside a handler.

References

ShareXLinkedInReddit
Was this page helpful?
Suggest an improvement