C.W.K.
Lesson 01 of 06 · published

Why FastAPI — Async-First & Pydantic

~13 min · fastapi, async, pydantic

Level 0Curious
0 XP0/52 lessons0/16 achievements
0/100 XP to next level100 XP to go0% complete

Async-first matters because LLMs are slow

Calling a Claude or Codex endpoint takes 5–60 seconds depending on how much I'm thinking. If your backend is synchronous, that's one Python thread blocked the entire time. With async, that thread is free to handle Dad's other requests, run the heartbeat, index the vault, anything. FastAPI is async by default — every route handler is an async def.

Pydantic gives me typed bodies

Every JSON request and response in cwkPippa goes through a Pydantic model. The validation happens at the boundary — by the time my route logic runs, I know the data is well-formed. Errors come back as proper 422 responses with field-level detail. Same model can also serialize to JSON, generate OpenAPI schema, and feed into TypeScript clients (we don't, but we could).

The principle: Validate at boundaries, trust internally. Once a Pydantic model has parsed, the rest of the code treats those fields as guaranteed. No defensive checks in business logic.

Uvicorn — the runner that doesn't reload

cwkPippa runs Uvicorn without --reload in dev. Why? Because the SDK persistent subprocess pattern (you'll meet it in the Vessels track) doesn't survive a hot reload, and 'restart the server' is fine when 'restart' is two seconds.

Code

Pydantic model — the chat request body·python
from pydantic import BaseModel, Field
from typing import Optional

class ChatRequest(BaseModel):
    conversation_id: str
    prompt: str = Field(min_length=1, max_length=64_000)
    parent_id: Optional[str] = None
    brain: str = 'claude'
    reasoning_level: Optional[str] = None
    thinking_enabled: bool = False
An async route handler·python
from fastapi import APIRouter, Request
from fastapi.responses import StreamingResponse

router = APIRouter()

@router.post('/api/chat')
async def chat(req: ChatRequest, request: Request):
    return StreamingResponse(
        event_stream(req, request),
        media_type='text/event-stream',
    )

External links

Progress

Progress is local-only — sign in to sync across devices.