FastAPI

Python modern API framework - type hints become docs and validation, async-native, as fast as Node/Go

TL;DR

One-liner: FastAPI is Python’s answer to building APIs fast - type hints become docs, validation, and speed.

Core Strengths:

  • Auto-generated docs - Swagger UI and ReDoc for free
  • Type-driven - Pydantic validation from type hints
  • Async native - built on Starlette, as fast as Node/Go
  • Developer friendly - excellent editor support and error messages

Core Concepts

Concept 1: Type Hints = Validation + Docs

Your type hints automatically become request validation, API documentation, and editor autocomplete.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

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

@app.post("/users/")
def create_user(user: User):  # Validated automatically
    return user  # Serialized automatically

Concept 2: Path Operations

Decorators define your HTTP endpoints:

@app.get("/users/{user_id}")    # GET with path param
@app.post("/users/")            # POST
@app.put("/users/{user_id}")    # PUT
@app.delete("/users/{user_id}") # DELETE

Concept 3: Dependency Injection

Reusable dependencies for auth, database connections, etc.

from fastapi import Depends

def get_current_user(token: str = Header()):
    return decode_token(token)

@app.get("/me")
def read_me(user = Depends(get_current_user)):
    return user

Quick Start

Install

pip install "fastapi[standard]"

Create main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def root():
    return {"message": "Hello FastAPI!"}

@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
    return {"item_id": item_id, "q": q}

Run

fastapi dev main.py
# Open http://localhost:8000
# Docs at http://localhost:8000/docs

Gotchas

Route order matters

# ❌ Wrong - /users/me is never matched
@app.get("/users/{user_id}")
def get_user(user_id: str): ...

@app.get("/users/me")  # Never reached!
def get_me(): ...

# ✅ Correct - specific routes first
@app.get("/users/me")
def get_me(): ...

@app.get("/users/{user_id}")
def get_user(user_id: str): ...

Async vs sync functions

# Use async for I/O operations
@app.get("/async")
async def async_endpoint():
    data = await fetch_from_db()  # Non-blocking
    return data

# Use sync for CPU-bound or blocking calls
@app.get("/sync")
def sync_endpoint():
    return heavy_computation()  # Runs in threadpool

Request body needs Pydantic model

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float
    description: str | None = None

@app.post("/items/")
def create_item(item: Item):  # Body is validated
    return item

# For raw body access
from fastapi import Request
@app.post("/raw")
async def raw_body(request: Request):
    body = await request.body()

Query params vs path params

# Path param: part of URL, required
@app.get("/users/{user_id}")  # /users/123
def get_user(user_id: int): ...

# Query param: after ?, optional by default
@app.get("/items/")  # /items/?skip=0&limit=10
def get_items(skip: int = 0, limit: int = 10): ...

When to Use

Best for:

  • REST APIs and microservices
  • ML model serving
  • Real-time applications (WebSockets)
  • Teams that love type safety

Not ideal for:

  • Full-stack apps with templates (use Django)
  • Simple scripts (overkill)
  • Legacy Python 2 projects

Comparison:

FeatureFastAPIDjangoFlask
SpeedVery fastModerateModerate
AsyncNativeLimitedLimited
Auto docsYesNoNo
Learning curveEasyMediumEasy

Next Steps

Cheatsheet

PatternCode
Path param@app.get("/items/{id}")
Query paramdef f(skip: int = 0)
Request bodydef f(item: Item)
Headerdef f(token: str = Header())
Cookiedef f(session: str = Cookie())
Response model@app.get("/", response_model=Item)
Status code@app.post("/", status_code=201)
Dependencydef f(db = Depends(get_db))
Background taskbackground_tasks.add_task(fn, arg)