Cheat SheetsFastAPIFundamentals

Fundamentals — Cheat Sheet

FastAPI · 6 topics. Download the PDF or the Instagram carousel and share it.

Cheat Sheet · AiCanCode.org
Fundamentals
FastAPI6 topicsQuick revision reference
1

FastAPI Introduction — Why FastAPI?

FastAPI is a modern, high-performance Python web framework where type hints do the work: one function signature gives you validation, serialization, and interactive API docs — on an async ASGI core.

  • Type hints drive everything: validation, conversion, serialization, docs
  • Built on Starlette (ASGI) + Pydantic (validation), served by Uvicorn
  • Async event loop → Node.js-class throughput for I/O-bound APIs
  • Auto-generated interactive docs at /docs — no YAML, never out of sync
Type hints in, validation + conversion + docs out
from fastapi import FastAPI

app = FastAPI()

@app.get("/orders/{order_id}")
def get_order(order_id: int, include_items: bool = False):
    return {"order_id": order_id, "include_items": include_items}

# GET /orders/42?include_items=true
#   → {"order_id": 42, "include_items": true}     types converted for you
# GET /orders/abc
#   → 422 {"detail": [{"loc": ["path", "order_id"],
#                      "msg": "Input should be a valid integer", ...}]}
#   ← you wrote ZERO validation code

# And for free, at /docs: interactive Swagger UI where anyone —
# frontend dev, tester, PM — can try this endpoint from the browser.

# The same in Flask: request.args.get(), manual int() + try/except,
# manual error response, separate Swagger YAML kept in sync by hand.
2

First App — FastAPI + Uvicorn in Ten Lines

pip install fastapi[standard], write main.py with an app and a path operation, run fastapi dev — you get hot reload, a live server, and Swagger docs at /docs in under a minute.

  • app = FastAPI(); path operations are decorated functions (@app.get/post/put/delete)
  • fastapi dev main.py (or uvicorn main:app --reload) for development
  • Return dicts/lists/models — JSON serialization is automatic
  • Develop against /docs — the Swagger UI is your live REST client
venv → install → main.py → fastapi dev: the whole loop
# Setup (once) — venv first, always
python -m venv .venv
.venv\Scripts\activate                  # Windows (source .venv/bin/activate on Linux)
pip install "fastapi[standard]"

# ── main.py ─────────────────────────────────
from fastapi import FastAPI

app = FastAPI(title="Campus Placement API", version="0.1.0")

@app.get("/")
def home():
    return {"service": "placement-api", "status": "up"}

@app.get("/health")
def health():
    return {"ok": True}

# Run it:
fastapi dev main.py                      # dev mode: auto-reload, on :8000
# or explicitly:
uvicorn main:app --reload --port 8000    # main:app = file main.py, object app

# Now open:
#   http://127.0.0.1:8000          → {"service":"placement-api","status":"up"}
#   http://127.0.0.1:8000/docs     → interactive Swagger UI
3

Path & Query Parameters — Typed and Validated

Function parameters that appear in the path are path params; the rest become query params. Type hints convert and validate both; Path() and Query() add constraints like ge=1 or max_length=50.

  • In the path → path param; has a default → query param; the signature decides
  • Type hints convert and validate; failures return structured 422s
  • Path()/Query() add ge/le, min_length/max_length, pattern + docs metadata
  • Fixed routes before parameterised ones; Enum params give fixed choices
The signature is the parser: path vs query decided by name + default
from fastapi import FastAPI

app = FastAPI()

# /trains/12345/coaches/S4?berth=upper&confirmed=true
@app.get("/trains/{train_no}/coaches/{coach}")
def coach_status(
    train_no: int,               # path — converted from "12345"
    coach: str,                  # path
    berth: str = "any",          # query — optional (has default)
    confirmed: bool = False,     # query — "true"/"True"/"1"/"yes" all work
):
    return {"train": train_no, "coach": coach, "berth": berth, "confirmed": confirmed}

# Optional query param that may be absent entirely:
@app.get("/students")
def search(branch: str | None = None, limit: int = 10):
    q = {"limit": limit}
    if branch:
        q["branch"] = branch
    return q

# Repeated query keys → list:  /tags?tag=python&tag=fastapi
@app.get("/tags")
def tags(tag: list[str] = []):
    return {"tags": tag}                 # ["python", "fastapi"]

# Route order matters — fixed paths BEFORE parameterised ones:
@app.get("/students/me")                 # must come first...
def current_student(): return {"me": True}

@app.get("/students/{student_id}")       # ...or this would capture "me"
def one_student(student_id: int): return {"id": student_id}
4

Request Body — Pydantic Models In

Declare a Pydantic BaseModel parameter and FastAPI parses the JSON body into a typed object — wrong shapes get a field-by-field 422 before your code runs, and the model doubles as the docs schema.

  • BaseModel parameter = JSON body: parsed, validated, typed — never use dict
  • 422 errors list every bad field with its exact location (loc)
  • Path + query + body mix freely in one signature; FastAPI sorts by type
  • model_dump(exclude_unset=True) distinguishes "not sent" from "sent as null"
One model: parsing + validation + autocomplete + docs schema
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class OrderIn(BaseModel):
    customer_name: str
    phone: str
    amount: float                      # rupees
    upi_id: str | None = None          # optional
    cod: bool = False                  # default

@app.post("/orders")
def create_order(order: OrderIn):      # ← body, because it's a BaseModel
    fee = 0 if order.amount >= 499 else 40
    return {
        "status": "created",
        "payable": order.amount + fee,
        "payment": "COD" if order.cod else (order.upi_id or "card"),
        "echo": order.model_dump(),    # model → dict
    }

# Valid request:
#   POST /orders  {"customer_name": "Asha", "phone": "9876543210", "amount": 599}
#   → 200, payable 599, payment "card"

# Invalid request:  {"customer_name": "Asha", "amount": "five hundred"}
#   → 422 with BOTH problems, precisely located:
#   [{"loc": ["body", "phone"],  "msg": "Field required"},
#    {"loc": ["body", "amount"], "msg": "Input should be a valid number"}]
5

Response Models & Status Codes — Shaping What Goes Out

response_model filters and documents what an endpoint returns — the classic win is UserOut without the password hash. status_code sets the right code per operation; HTTPException handles the sad paths.

  • response_model filters output — extra fields (password!) are dropped, guaranteed
  • Separate In/Out models sharing a base class is the standard pattern
  • Set status_code per operation: 201 create, 204 delete, 200 default
  • raise HTTPException(status_code, detail) from any depth for error responses
In-model with password, Out-model without — leak-proof by design
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()

class UserBase(BaseModel):
    name: str
    email: EmailStr

class UserIn(UserBase):               # what the client SENDS
    password: str

class UserOut(UserBase):              # what the client GETS — no password
    id: int

fake_db = {}

@app.post("/users", response_model=UserOut, status_code=201)
def register(user: UserIn):
    hashed = "bcrypt$" + user.password[::-1]        # (real hashing → Auth chapter)
    record = {"id": len(fake_db) + 1, **user.model_dump(), "password": hashed}
    fake_db[record["id"]] = record
    return record        # ← contains password! response_model strips it.

# POST /users {"name":"Asha","email":"asha@iitb.ac.in","password":"s3cret"}
# → 201 {"id": 1, "name": "Asha", "email": "asha@iitb.ac.in"}
#   password never leaves the server — enforced by the framework, not discipline

# Lists work too:
@app.get("/users", response_model=list[UserOut])
def all_users():
    return list(fake_db.values())

# Skip nulls in output:  response_model_exclude_none=True
6

Auto Docs — Swagger UI, ReDoc & OpenAPI

Every FastAPI app ships /docs (Swagger UI), /redoc, and /openapi.json generated from your code — enrich them with tags, summaries, and examples, and lock them down in production.

  • /docs (interactive), /redoc (reading), /openapi.json (the machine spec)
  • Docs derive from code — they cannot drift out of date
  • tags group routes; summary/description/examples enrich the UI
  • Disable or gate docs URLs in production (docs_url=None)
Docs written where they cannot rot: in the route and the model
from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI(
    title="Campus Placement API",
    version="1.2.0",
    description="Drives the placement portal: students, drives, offers.",
)

class DriveIn(BaseModel):
    company: str = Field(description="Recruiting company", examples=["Infosys"])
    ctc_lpa: float = Field(ge=1, le=100, description="Package in LPA", examples=[6.5])
    min_cgpa: float = Field(default=6.0, ge=0, le=10)

    model_config = {
        "json_schema_extra": {
            "examples": [{"company": "TCS", "ctc_lpa": 7.0, "min_cgpa": 6.5}]
        }
    }

@app.post(
    "/drives",
    tags=["Drives"],                          # section heading in /docs
    summary="Create a placement drive",
    description="Registers a company drive. Students below min_cgpa will not see it.",
    response_description="The created drive",
)
def create_drive(drive: DriveIn):
    return drive

@app.get("/drives", tags=["Drives"], summary="List all drives")
def list_drives():
    return []

# http://127.0.0.1:8000/docs         Swagger UI — grouped, described, try-it-out
# http://127.0.0.1:8000/redoc        ReDoc — reference-style reading
# http://127.0.0.1:8000/openapi.json The spec — feed to generators/gateways
Learn this free with Aria, your AI tutor → AiCanCode.org/learn/fastapi