Web Frameworks: Flask vs Django vs FastAPI
📖 Concept
Python offers three dominant web frameworks, each with a distinct philosophy and sweet spot. Understanding their differences is critical for choosing the right tool and answering interview questions about architecture trade-offs.
Comparison Table:
| Feature | Flask | Django | FastAPI |
|---|---|---|---|
| Philosophy | Micro-framework, minimal core | Batteries-included | Modern, async-first |
| Server Interface | WSGI | WSGI (ASGI via Channels) | ASGI (native async) |
| ORM | None (use SQLAlchemy) | Built-in Django ORM | None (use SQLAlchemy) |
| Admin Panel | None | Built-in | None |
| Validation | Manual / WTForms | Django Forms / DRF serializers | Pydantic (automatic) |
| Auto Docs | No (add Swagger manually) | No (add via DRF) | Yes (OpenAPI + Swagger UI) |
| Async Support | Limited (async views in 2.0+) |
Partial (views, ORM still sync) | Full native async/await |
| Learning Curve | Low | High | Medium |
| Best For | Microservices, prototypes | Full-stack apps, CMS, admin-heavy | APIs, high-concurrency services |
WSGI vs ASGI:
- WSGI (Web Server Gateway Interface) is the traditional synchronous protocol. One request per thread. Servers: Gunicorn, uWSGI.
- ASGI (Asynchronous Server Gateway Interface) supports async, WebSockets, and long-lived connections natively. Servers: Uvicorn, Daphne, Hypercorn.
When to use each:
- Flask — small APIs, microservices, when you want full control over every dependency. Perfect for teams that prefer explicit over implicit.
- Django — content-heavy sites, admin dashboards, projects needing auth, ORM, migrations, and templating out of the box. Ideal when development speed matters more than micro-optimization.
- FastAPI — high-performance REST/GraphQL APIs, real-time applications, ML model serving. Best when you need automatic validation, serialization, and interactive documentation.
In interviews, emphasize that the choice depends on project requirements — not personal preference. A monolithic Django app and a FastAPI microservice solve different problems.
💻 Code Example
1# ============================================================2# Flask — Minimal "Hello World" API3# ============================================================4# from flask import Flask, jsonify, request, abort5#6# app = Flask(__name__)7#8# # In-memory store (replaced by a database in production)9# books = [10# {"id": 1, "title": "Clean Code", "author": "Robert Martin"},11# {"id": 2, "title": "Pragmatic Programmer", "author": "Hunt & Thomas"},12# ]13#14#15# @app.route("/api/books", methods=["GET"])16# def get_books():17# """GET /api/books — return all books."""18# return jsonify(books)19#20#21# @app.route("/api/books/<int:book_id>", methods=["GET"])22# def get_book(book_id):23# """GET /api/books/:id — return single book or 404."""24# book = next((b for b in books if b["id"] == book_id), None)25# if book is None:26# abort(404, description="Book not found")27# return jsonify(book)28#29#30# @app.route("/api/books", methods=["POST"])31# def create_book():32# """POST /api/books — create a new book."""33# data = request.get_json()34# if not data or "title" not in data:35# abort(400, description="Title is required")36# new_book = {37# "id": max(b["id"] for b in books) + 1 if books else 1,38# "title": data["title"],39# "author": data.get("author", "Unknown"),40# }41# books.append(new_book)42# return jsonify(new_book), 20143#44#45# # ============================================================46# # Django — Views equivalent (views.py in a Django app)47# # ============================================================48# # BAD: Fat views with no separation of concerns49# # from django.http import JsonResponse50# # from django.views import View51# #52# # class BookView(View):53# # def get(self, request):54# # books = list(Book.objects.values("id", "title", "author"))55# # return JsonResponse(books, safe=False)56# # def post(self, request):57# # import json58# # data = json.loads(request.body)59# # book = Book.objects.create(**data) # No validation!60# # return JsonResponse({"id": book.id}, status=201)61#62# # GOOD: Django REST Framework serializer-based views63# # from rest_framework import viewsets, serializers64# # from .models import Book65# #66# # class BookSerializer(serializers.ModelSerializer):67# # class Meta:68# # model = Book69# # fields = ["id", "title", "author"]70# #71# # class BookViewSet(viewsets.ModelViewSet):72# # queryset = Book.objects.all()73# # serializer_class = BookSerializer74# # # Gives you GET, POST, PUT, PATCH, DELETE for free75#76#77# # ============================================================78# # FastAPI — Same API with automatic validation & docs79# # ============================================================80# from fastapi import FastAPI, HTTPException81# from pydantic import BaseModel, Field82#83# app = FastAPI(title="Book API", version="1.0.0")84#85#86# class BookCreate(BaseModel):87# """Pydantic model = automatic validation + serialization."""88# title: str = Field(..., min_length=1, max_length=200)89# author: str = Field(default="Unknown", max_length=100)90#91#92# class BookResponse(BookCreate):93# id: int94#95#96# books_db: list[BookResponse] = []97#98#99# @app.get("/api/books", response_model=list[BookResponse])100# async def get_books():101# return books_db102#103#104# @app.get("/api/books/{book_id}", response_model=BookResponse)105# async def get_book(book_id: int):106# book = next((b for b in books_db if b.id == book_id), None)107# if not book:108# raise HTTPException(status_code=404, detail="Book not found")109# return book110#111#112# @app.post("/api/books", response_model=BookResponse, status_code=201)113# async def create_book(book: BookCreate):114# # Pydantic validates the request body automatically115# new_id = max((b.id for b in books_db), default=0) + 1116# new_book = BookResponse(id=new_id, **book.model_dump())117# books_db.append(new_book)118# return new_book119#120#121# # Run: uvicorn main:app --reload122# # Docs: http://127.0.0.1:8000/docs (Swagger UI auto-generated)
🏋️ Practice Exercise
Exercises:
Build the same CRUD API (Create, Read, Update, Delete for a `Task` model with `id`, `title`, `done`, `created_at` fields) in all three frameworks: Flask, Django REST Framework, and FastAPI. Compare the total lines of code, validation handling, and error responses.
Add pagination to each framework's list endpoint. Implement `?page=1&per_page=10` query parameters. Compare how each framework handles query parameter parsing and validation.
Create a Flask Blueprint and a Django app that both serve a `/health` endpoint returning `{"status": "ok", "uptime":
}`. Demonstrate how each framework organizes modular code. Write a FastAPI app with automatic OpenAPI documentation. Add custom examples to Pydantic models using `model_config` so that the Swagger UI shows realistic sample data.
Deploy your FastAPI app behind Uvicorn with Gunicorn as the process manager. Benchmark it with `wrk` or `hey` and compare throughput against the Flask equivalent running under Gunicorn with sync workers.
⚠️ Common Mistakes
Using Flask for a project that needs auth, admin, ORM, and migrations out of the box — Django would save weeks of integration work. Choose the framework that matches your project scope, not the one you are most comfortable with.
Running FastAPI with a WSGI server like Gunicorn without Uvicorn workers. FastAPI requires an ASGI server. Use
uvicorn main:apporgunicorn main:app -k uvicorn.workers.UvicornWorkerfor production.Blocking the event loop in FastAPI async handlers by calling synchronous I/O (e.g.,
time.sleep(), synchronous DB queries). Useawait asyncio.sleep(), async DB drivers, or declare the handler asdef(notasync def) so FastAPI runs it in a thread pool.Not validating request data in Flask. Unlike FastAPI (Pydantic) or Django (Forms/Serializers), Flask does zero automatic validation. Always validate
request.get_json()manually or use a library like Marshmallow.Ignoring Django's CSRF protection when building APIs. For token-based APIs, use Django REST Framework's authentication classes and explicitly exempt views from CSRF where appropriate, rather than disabling middleware globally.
💼 Interview Questions
🎤 Mock Interview
Practice a live interview for Web Frameworks: Flask vs Django vs FastAPI