Web Frameworks#

Python powers some of the most popular web frameworks. This section covers the three main options — FastAPI for modern async APIs, Flask for lightweight applications, and Django for full-featured projects — and helps you choose between them.


The Python Web Ecosystem#

FrameworkTypeBest ForAsync
FastAPIMicroAPIs, microservicesNative (ASGI)
FlaskMicroSimple apps, prototypesVia extensions
DjangoFull-stackContent sites, admin-heavy appsPartial (Django 4.1+)
StarletteMicroFastAPI’s foundationNative (ASGI)
LitestarMicroFastAPI alternativeNative (ASGI)

WSGI vs ASGI#

WSGIASGI
ModelSynchronousAsynchronous
ConcurrencyThread-per-requestEvent loop + async
ServersGunicorn, uWSGIUvicorn, Hypercorn
FrameworksFlask, Django (traditional)FastAPI, Starlette, Django (channels)
WebSocketsNot supportedSupported

FastAPI#

Modern, fast, type-safe API framework built on Starlette and Pydantic:

from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel, EmailStr

app = FastAPI(title="User API", version="1.0.0")

class UserCreate(BaseModel):
    name: str
    email: EmailStr
    age: int | None = None

class UserResponse(BaseModel):
    id: int
    name: str
    email: str

@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int):
    user = await db.find_user(user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user

@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(user: UserCreate):
    return await db.create_user(user)

@app.get("/users", response_model=list[UserResponse])
async def list_users(skip: int = 0, limit: int = 20):
    return await db.list_users(skip=skip, limit=limit)

Key Features#

FeatureHow
Auto-generated OpenAPI docs/docs (Swagger UI), /redoc
Request validationPydantic models — automatic 422 on invalid input
Dependency injectionDepends() for DB sessions, auth, etc.
Async nativeasync def endpoints with await
Type-checkedEditor autocomplete, mypy compatible

Dependency Injection#

from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession

async def get_db() -> AsyncGenerator[AsyncSession, None]:
    async with async_session() as session:
        yield session

@app.get("/users/{id}")
async def get_user(id: int, db: AsyncSession = Depends(get_db)):
    return await db.get(User, id)

Flask#

Lightweight, flexible, minimal opinions:

from flask import Flask, jsonify, request, abort

app = Flask(__name__)

@app.route("/users/<int:user_id>", methods=["GET"])
def get_user(user_id):
    user = db.find_user(user_id)
    if not user:
        abort(404)
    return jsonify(user.to_dict())

@app.route("/users", methods=["POST"])
def create_user():
    data = request.get_json()
    user = db.create_user(data["name"], data["email"])
    return jsonify(user.to_dict()), 201

if __name__ == "__main__":
    app.run(debug=True)

Flask Ecosystem#

ExtensionPurpose
Flask-SQLAlchemyORM integration
Flask-MigrateDatabase migrations (Alembic)
Flask-LoginSession-based authentication
Flask-CORSCross-Origin Resource Sharing
Flask-RESTfulREST API utilities
Flask-LimiterRate limiting

Django#

Full-featured “batteries included” framework:

# models.py
from django.db import models

class User(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True)
    created_at = models.DateTimeField(auto_now_add=True)

# views.py
from django.http import JsonResponse
from django.views import View

class UserDetailView(View):
    def get(self, request, user_id):
        user = User.objects.get(id=user_id)
        return JsonResponse({"id": user.id, "name": user.name})

# urls.py
urlpatterns = [
    path("users/<int:user_id>/", UserDetailView.as_view()),
]

Django’s Batteries#

FeatureBuilt-in?
ORM✓ (Django ORM)
Admin interface✓ (auto-generated CRUD UI)
Authentication✓ (users, groups, permissions)
Migrations✓ (manage.py makemigrations)
Forms / validation✓
Template engine✓
CSRF protection✓
Testing client✓
Django REST FrameworkSeparate package (DRF) for APIs

Choosing a Framework#

CriterionFastAPIFlaskDjango
API-only service✓✓✓✓✓✓ (with DRF)
Full web application✓✓✓✓✓✓
Learning curveLowLowMedium
Admin interface——✓✓✓
Async supportNativeLimitedPartial
Auto documentation✓✓✓ (OpenAPI)ManualManual (DRF has it)
Type safety✓✓✓——
Community sizeGrowing fastLargeVery large
DeploymentUvicornGunicornGunicorn / Daphne

Quick decision:

  • Building a JSON API → FastAPI
  • Quick prototype or microservice → Flask
  • Full application with admin, auth, ORM → Django

Key Takeaways#

  • FastAPI is the modern default for Python APIs — auto-validation, auto-docs, native async, type-safe.
  • Flask is minimal and flexible — good for prototypes and small services. Add extensions as needed.
  • Django is “batteries included” — ORM, admin, auth, migrations built in. Ideal for content-heavy applications.
  • ASGI (Uvicorn) for async frameworks; WSGI (Gunicorn) for sync. FastAPI requires ASGI.
  • Pydantic models in FastAPI serve as request validation, response serialisation, and documentation in one.
  • All three frameworks are production-ready. Choose based on your project needs, not hype.