What is FastAPI — ASGI, Pydantic and Automatic Docs

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}
Note: FastAPI is an ASGI (Asynchronous Server Gateway Interface) framework, which means it runs on an async event loop managed by a server like Uvicorn. This is the same async model covered in Chapter 11 — an event loop that handles many concurrent requests by switching between them during I/O waits. This is why FastAPI route handlers can be either 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.
Tip: Visit 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.
Warning: FastAPI is an API framework, not a full-stack web framework — it does not include a templating engine, static file server, session management, or authentication system out of the box. For a complete application you add these pieces yourself: Jinja2 for templates, 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)

🧠 Test Yourself

What does FastAPI generate automatically from your Python type hints and Pydantic models, without any extra configuration?