Starters
- What is FastAPI?0:50
- Margherita: Hello World0:50
Python Pizza Istanbul · 14 November 2026
FastAPI basics in 10 minutes. Served hot.
Opens fullscreen. Use the arrow keys or a presentation clicker.
Click one and the talk opens right there.
10 minutes, 14 slides, zero pineapple.
PYTHON PIZZA ISTANBUL · 14 NOV 2026
In 10 minutes, still hot.
Today’s menu
A modern, fast, type-hint-driven Python web framework.
automatic documentation
binds it all together
validation & serialization
ASGI, routing, async
Three lines, one working API.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root() -> dict[str, str]:
return {"message": "Merhaba Python Pizza! 🍕"} $ uv run fastapi dev examples/01_hello.py ⚡️ Starting FastAPI in development mode 🌐 Server started at http://127.0.0.1:8000 Documentation at http://127.0.0.1:8000/docs
{
"message": "Merhaba Python Pizza! 🍕"
} Where does the data come from? One function, four sources.
@app.post("/pizzas/{pizza_id}/orders")
async def order_pizza(
pizza_id: int, # path
order: Order, # body
extra_cheese: bool = False, # query
x_table_no: Annotated[int | None, Header()] = None, # header
) -> dict[str, object]:
return {
"pizza_id": pizza_id,
"extra_cheese": extra_cheese,
"table": x_table_no,
"order": order,
} /pizzas/3/orders → pizza_id = 3 ?extra_cheese=true → extra_cheese = True X-Table-No: 7 → x_table_no = 7 {"customer": "Ayşe", "quantity": 2} → order = Order(…) 422 pizza_id: int → a request to /pizzas/abc is rejected before your function runs.
We wrote no validation code. Pineapple was still rejected.
class Topping(StrEnum):
mozzarella = "mozzarella"
pepperoni = "pepperoni"
mushroom = "mushroom"
basil = "basil" # pineapple? not on this menu!
class PizzaCreate(BaseModel):
name: str = Field(min_length=2, max_length=40)
price: int = Field(gt=0)
toppings: list[Topping] = Field(min_length=1) /pizzasValid request{"name": "Margherita", "price": 190, "toppings": ["mozzarella", "basil"]}{
"name": "Margherita",
"price": 190,
"toppings": ["mozzarella", "basil"]
}/pizzasPineapple request{"name": "Hawaii", "price": 200, "toppings": ["mozzarella", "ananas"]}{
"detail": [{
"loc": ["body", "toppings", 1],
"msg": "Input should be 'mozzarella', 'pepperoni', 'mushroom' or 'basil'",
"input": "ananas"
}]
}Without writing a single line of docs.
/docs Swagger UI /redoc ReDoc /openapi.json machine-readable schema
The chef just makes pizza. The kitchen brings the ingredients.
def pagination(limit: int = 10, offset: int = 0) -> dict[str, int]:
return {"limit": limit, "offset": offset}
Pagination = Annotated[dict[str, int], Depends(pagination)]
@app.get("/menu")
async def menu(page: Pagination): ...
def require_chef(x_api_key: Annotated[str | None, Header()] = None):
if x_api_key != "chef-secret":
raise HTTPException(401, "Only the chef can do that 👨🍳")
@app.delete("/menu/{name}", dependencies=[Depends(require_chef)])
async def remove(name: str): ... yield = cleanup when done. FastAPI does the dishes 🧽
pagination() limit & offset require_chef() authorization get_db() database session async def get_db() -> AsyncIterator[AsyncSession]:
async with SessionLocal() as session:
yield session Three orders at once. How many waiters do you need?
@app.get("/bake/async")
async def bake_async():
await asyncio.sleep(BAKE_SECONDS) # waiter serves other tables ✅
@app.get("/bake/blocking")
async def bake_blocking():
time.sleep(BAKE_SECONDS) # waiter freezes at the oven ❌
@app.get("/bake/threadpool")
def bake_threadpool():
time.sleep(BAKE_SECONDS) # `def` runs in a thread pool ✅ ⏱ Measured for real: 3 concurrent requests
awaitserve other tables while the oven worksdefFastAPI runs you in a thread poolasync def + time.sleep()the waiter freezes at the ovenRequests flow from top to bottom.
fastapi-pizza/
TestClient: no server needed, done in seconds.
def test_pineapple_is_rejected(client: TestClient, chef: dict[str, str]) -> None:
body = {"name": "Hawaii", "price": 200, "toppings": ["mozzarella", "ananas"]}
response = client.post(URL, json=body, headers=chef)
assert response.status_code == 422
error = response.json()["detail"][0]
assert error["loc"] == ["body", "toppings", 1]
assert error["input"] == "ananas"
assert client.get(URL).json() == [] # nothing was created $ uv run pytest -v tests/test_pizzas.py::test_pizza_id_must_be_an_int PASSED [ 65%] tests/test_pizzas.py::test_pineapple_is_rejected PASSED [ 70%] ============================== 44 passed in 2.07s ==============================
From local to live in three commands.
$ fastapi dev development auto-reload
$ fastapi run production one command, ready server
$ fastapi deploy FastAPI Cloud live in one command
Docker or plain uvicorn on your own server work too.
14 November 2026 · StartGate, Maslak, Istanbul
Tip: ⭐ the repo
Questions? (No pineapple.)
python-pizza.erhan.dev/en github.com/erhan/fastapi-pizza