FastAPI Cheatsheet

Request Body (Pydantic)

Use this FastAPI reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Basic Request Body

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float
    is_offer: bool = False   # optional with default

@app.post("/items")
def create_item(item: Item):
    return item

FastAPI reads the request body as JSON, validates it against Item, and injects the typed object.

Field Types and Defaults

from typing import Optional, List, Dict, Any
from pydantic import BaseModel, Field
from datetime import datetime
from decimal import Decimal
from uuid import UUID

class Product(BaseModel):
    id: UUID
    name: str
    description: Optional[str] = None   # optional, defaults to None
    price: Decimal
    tags: List[str] = []
    metadata: Dict[str, Any] = {}
    created_at: datetime

Field() — Validation and Docs Metadata

from pydantic import BaseModel, Field

class Item(BaseModel):
    name: str = Field(
        ...,                         # required (Ellipsis)
        min_length=1,
        max_length=100,
        title="Item name",
        description="Unique name of the item",
        examples=["Widget"],
    )
    price: float = Field(
        ...,
        gt=0,
        description="Must be positive",
    )
    discount: float = Field(default=0.0, ge=0.0, le=1.0)
    sku: str = Field(default=None, pattern=r"^[A-Z]{3}-\d{4}$")

Nested Models

class Address(BaseModel):
    street: str
    city: str
    zip_code: str

class User(BaseModel):
    name: str
    email: str
    address: Address        # nested
    addresses: List[Address] = []  # list of nested
# JSON sent:
# {"name": "Alice", "email": "a@b.com",
#  "address": {"street": "1 Main St", "city": "NYC", "zip_code": "10001"}}

Multiple Body Parameters

class Item(BaseModel):
    name: str
    price: float

class User(BaseModel):
    username: str

# FastAPI expects: {"item": {...}, "user": {...}}
@app.put("/items/{id}")
def update(id: int, item: Item, user: User):
    return {"item": item, "user": user}

Body() — Singular Extra Body Fields

from fastapi import Body

@app.put("/items/{id}")
def update(
    id: int,
    item: Item,
    importance: int = Body(..., ge=1, le=5),
):
    return {"item": item, "importance": importance}
# expects: {"item": {...}, "importance": 3}

embed=True — Force Named Key

# Without embed, FastAPI expects: {"name": "foo", "price": 1.0}
# With embed=True:               {"item": {"name": "foo", "price": 1.0}}
@app.post("/items")
def create(item: Item = Body(..., embed=True)):
    return item

model_config and Settings

from pydantic import BaseModel, ConfigDict

class Item(BaseModel):
    model_config = ConfigDict(
        str_strip_whitespace=True,  # strip leading/trailing spaces
        str_to_upper=False,
        populate_by_name=True,      # allow both alias and field name
        extra="forbid",             # reject unknown fields (default: "ignore")
        frozen=True,                # immutable instances
    )
    name: str
    price: float

Aliases and Serialization

from pydantic import BaseModel, Field

class Item(BaseModel):
    item_name: str = Field(..., alias="itemName")   # JSON key is "itemName"

# Parsing:  Item.model_validate({"itemName": "Widget"})
# Output:   item.model_dump(by_alias=True) → {"itemName": "Widget"}

Schema Examples

class Item(BaseModel):
    name: str
    price: float

    model_config = ConfigDict(
        json_schema_extra={
            "examples": [
                {"name": "Widget", "price": 9.99},
            ]
        }
    )

Custom Validators (Pydantic v2)

from pydantic import BaseModel, field_validator, model_validator

class Item(BaseModel):
    name: str
    price: float
    discount: float = 0.0

    @field_validator("name")
    @classmethod
    def name_must_not_be_empty(cls, v: str) -> str:
        if not v.strip():
            raise ValueError("name cannot be blank")
        return v.title()

    @model_validator(mode="after")
    def discount_lt_price(self) -> "Item":
        if self.discount >= self.price:
            raise ValueError("discount must be less than price")
        return self

Parsing and Serializing Manually

# dict → model
item = Item.model_validate({"name": "Widget", "price": 9.99})

# JSON string → model
item = Item.model_validate_json('{"name": "Widget", "price": 9.99}')

# model → dict
d = item.model_dump()
d = item.model_dump(exclude_unset=True)   # only fields explicitly set
d = item.model_dump(exclude_none=True)
d = item.model_dump(include={"name"})
d = item.model_dump(exclude={"password"})

# model → JSON string
j = item.model_dump_json()

# JSON schema
schema = Item.model_json_schema()

Form Data (not JSON)

from fastapi import Form

# requires: pip install python-multipart
@app.post("/login")
def login(username: str = Form(...), password: str = Form(...)):
    return {"username": username}
# Content-Type: application/x-www-form-urlencoded

You cannot mix Form and a Pydantic body model in the same endpoint — they use different content types.

File Uploads

from fastapi import File, UploadFile

@app.post("/upload")
async def upload(file: UploadFile = File(...)):
    contents = await file.read()
    return {"filename": file.filename, "size": len(contents)}

# Multiple files:
@app.post("/uploads")
async def multi_upload(files: List[UploadFile] = File(...)):
    return [{"filename": f.filename} for f in files]

UploadFile Attributes

AttributeTypeDescription
filenamestroriginal filename
content_typestrMIME type
sizeint | Nonefile size in bytes
headersHeadersraw headers
fileSpooledTemporaryFilefile-like object

UploadFile Methods

MethodReturnsNotes
await file.read(size)bytesread up to size bytes
await file.write(data)Nonewrite bytes
await file.seek(offset)Noneseek to position
await file.close()Noneclose (auto on response)