FastAPI is a modern Python web framework for building APIs, released in 2018 and now one of the most widely adopted Python frameworks. It is built on two pillars: Starlette (an ASGI framework for async request handling) and Pydantic (for data validation using Python type hints). The combination produces a framework where your type annotations do triple duty — they document the API, validate incoming data, and power IDE autocompletion — all without any extra code. FastAPI is consistently benchmarked as one of the fastest Python frameworks, on par with NodeJS and Go for I/O-bound workloads.
FastAPI’s Core Value Proposition
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class PostCreate(BaseModel):
title: str
body: str
# This one function definition gives you:
# 1. POST /posts endpoint
# 2. Automatic JSON body parsing (title and body from request)
# 3. Automatic Pydantic validation (422 if title or body missing/wrong type)
# 4. Response serialised to JSON automatically
# 5. Entry in /docs Swagger UI — fully documented, testable in browser
# 6. Entry in /openapi.json — machine-readable OpenAPI schema
@app.post("/posts", status_code=201)
def create_post(post: PostCreate):
return {"id": 1, "title": post.title, "body": post.body}
async def (runs directly on the event loop) or plain def (runs in a thread pool), as you learned in Chapter 11’s final lesson.http://localhost:8000/docs while your FastAPI app is running to get a fully interactive Swagger UI — you can send real requests, see request/response schemas, and test every endpoint without writing a single line of client code. /redoc provides an alternative ReDoc documentation view. Both are generated automatically from your code — no extra configuration needed. This interactive documentation is one of FastAPI’s most compelling features for teams and API consumers.StaticFiles for assets, a JWT library for authentication, and so on. This is intentional — FastAPI focuses on being the best API layer, composing with best-of-breed libraries for each concern.FastAPI vs Flask vs Django REST Framework
| Feature | FastAPI | Flask + Marshmallow | Django REST Framework |
|---|---|---|---|
| Async support | Native (ASGI) | Limited (WSGI) | Limited (WSGI) |
| Request validation | Automatic (Pydantic) | Manual (Marshmallow) | Serializer classes |
| API documentation | Automatic (OpenAPI) | Manual / extensions | Manual / drf-spectacular |
| Type hints | First-class, drives all | Optional | Optional |
| Performance | Very high (async) | Moderate (sync) | Moderate (sync) |
| Learning curve | Moderate | Low | High (Django ecosystem) |
| Best for | APIs, microservices | Small APIs, learning | Full-featured web APIs |
How FastAPI Uses Type Hints
from fastapi import FastAPI, Path, Query
from pydantic import BaseModel
from typing import Literal
app = FastAPI()
# Type hints control EVERYTHING in FastAPI:
# - Path parameter declared as int → FastAPI validates it's an integer
# - Query parameter with default → optional in URL
# - Body parameter as BaseModel → parsed from JSON, validated
# - Return type annotation → informs OpenAPI schema
@app.get("/posts/{post_id}")
def get_post(
post_id: int, # path param — must be integer
include_body: bool = False, # query param — optional, default False
):
return {"id": post_id, "include_body": include_body}
# GET /posts/42 → post_id=42, include_body=False
# GET /posts/42?include_body=true → post_id=42, include_body=True
# GET /posts/abc → 422 Validation Error (abc is not int)
# GET /posts/42?include_body=maybe → 422 Validation Error (maybe not bool)
The OpenAPI Specification
from fastapi import FastAPI
app = FastAPI(
title = "Blog API",
description = "A blog API built with FastAPI, PostgreSQL and React",
version = "1.0.0",
# Customise doc URLs
docs_url = "/docs", # Swagger UI
redoc_url = "/redoc", # ReDoc
openapi_url = "/openapi.json", # raw schema
)
# Decorating a route with tags groups it in the docs:
@app.get("/posts", tags=["Posts"])
def list_posts(): ...
@app.post("/posts", tags=["Posts"])
def create_post(): ...
@app.get("/users", tags=["Users"])
def list_users(): ...
# /docs shows Posts and Users as separate sections
Common Mistakes
Mistake 1 — Disabling docs in development
❌ Wrong — turning off docs while building:
app = FastAPI(docs_url=None, redoc_url=None) # no docs — slows development!
✅ Correct — enable in development, optionally disable in production:
import os
docs_url = "/docs" if os.getenv("ENVIRONMENT") != "production" else None
app = FastAPI(docs_url=docs_url)
Mistake 2 — Confusing FastAPI with a full-stack framework
❌ Wrong — expecting built-in session management, HTML rendering, etc.
✅ Correct — FastAPI is an API framework; add libraries as needed (Jinja2 for templates, itsdangerous for sessions, etc.).
Mistake 3 — Using Flask patterns in FastAPI (sync blocking in async handlers)
❌ Wrong — synchronous database call in async handler (Chapter 11 anti-pattern):
@app.get("/posts")
async def get_posts():
return db.execute("SELECT * FROM posts").fetchall() # blocking!
✅ Correct — use sync def with sync DB, or async def with async DB (Chapter 11).
Quick Reference
| Concept | FastAPI Approach |
|---|---|
| Framework type | ASGI, async-first |
| Validation | Pydantic, driven by type hints |
| API docs | Automatic at /docs and /redoc |
| Schema | OpenAPI 3.x at /openapi.json |
| Server | Uvicorn (ASGI), Gunicorn+Uvicorn (production) |
| Route handler | async def (event loop) or def (thread pool) |