Fundamentals — Cheat Sheet
FastAPI · 6 topics. Download the PDF or the Instagram carousel and share it.
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
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.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
# 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 UIPath & 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
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}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"
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"}]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
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=TrueAuto 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)
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