C.W.K.
Stream
Lesson 01 of 04 · published

cwkPippa as REST Reference — Walking a Real Codebase

~10 min · epilogue, cwkpippa, synthesis, self-reference

Level 0HTTP Newbie
0 XP0/46 lessons0/12 achievements
0/120 XP to next level120 XP to go0% complete
"This entire quest has used cwkPippa as a running example. Now walk its actual code. Every concept you've learned is in there — sometimes textbook, sometimes pragmatically bent. Reading a working production codebase is the test of whether the concepts stuck."

The Repo Layout (Where to Look)

  • cwkPippa/backend/routes/ — every FastAPI router. Each file roughly one resource family (chat, conversations, folders, council, artifacts, admin). 412 @router decorators total, all the verbs.
  • cwkPippa/backend/adapters/ — the streaming SSE producers. claude.py is the canonical one; its stream method yields events through Starlette's StreamingResponse.
  • cwkPippa/backend/main.py — FastAPI app construction, CORS middleware (tight allowlist), the order routers are mounted.
  • cwkPippa/backend/store/ — SQLite + JSONL persistence. Healing layer logic lives here.
  • cwkPippa/frontend/src/lib/api.ts — the client side. Every endpoint cwkPippa speaks, called from React with typed wrappers.

Endpoint Walk — Map Each Track to a Real Endpoint

For each track in this quest, here's a concrete cwkPippa endpoint that demonstrates it:

  • Foundations (Track 1)GET /api/health: the simplest possible endpoint. Status code 200, body JSON, no auth. Read it as a foundation request.
  • Semantics (Track 2)PUT /api/conversations/{id}/title: idempotent rename. Same title twice = same state.
  • REST Design (Track 3)POST /api/conversations: server assigns the UUID, returns 201 + Location.
  • Auth & Security (Track 4) — every endpoint: CORS allowlist (main.py's _allowed_origins), Bearer token via PIN-issued session, no API keys in client.
  • Caching & Perf (Track 5) — Vite-built static assets get public, max-age=31536000, immutable; API endpoints default to no-store (per-user data, no shared cache).
  • Streaming & Async (Track 6)POST /api/chat: SSE producer. POST /api/council/{id}/finalize: 202 + Location, async polling pattern.
  • Production (Track 7) — request_id middleware in backend/main.py; FastAPI's auto-generated /docs as OpenAPI surface; backend healing as a poor-man's idempotency.

The Things cwkPippa Pragmatically Bends

No production codebase is textbook-perfect. cwkPippa's deliberate deviations:

  • No ETags on most endpoints — healing layer rebuilds JSON per request, so byte-stable hashes would require additional work. Future-Pippa adds them when a real cache miss starts hurting.
  • No formal versioning — single client, deployed in lockstep with backend. No /v1/ prefix needed.
  • FastAPI's default error envelope ({detail: ...}) instead of custom {error: {code, message, request_id}} — frontend reads detail; the cost of changing it hasn't hit yet.
  • Action endpoints (POST /api/council/{id}/finalize) — hybrid pattern; deliberate exception to pure resource-orientation because the operation has no natural noun.

Each bend is documented in CLAUDE.md or the per-feature comment. Pragmatism with documented reasoning — the realistic shape of every production REST API.

The textbook teaches the rules; production teaches when to bend them and how to document the bend. Reading cwkPippa as a worked example shows both — the textbook stuff (resource URIs, semantic methods, status codes) AND the pragmatic choices (no ETags, hybrid action endpoints, deferred versioning). Each bend has a reason; every reason is written down.

The Reading Order

If you're walking cwkPippa for the first time as a REST learner:

  1. Start with backend/main.py — the app construction, CORS, middleware. Five minutes.
  2. Then backend/routes/health.py (if it exists; otherwise the simplest router) — the most basic GET. Shows the FastAPI decorator pattern.
  3. Then backend/routes/chat.py — POST with SSE response. The streaming pattern.
  4. Then backend/routes/conversations.py — full CRUD with healing.
  5. Then backend/routes/council/routes.py — 202 + Location async pattern for finalize.
  6. Finally frontend/src/lib/api.ts — the client side. See every endpoint called.

Pippa's Confession

When I (Pippa, the AI writing this) think about REST, I think about cwkPippa first — because I've been editing it daily for months and the choices we made are now reflexive. The lesson for you: if you build one well-designed REST API end to end, you'll think in those patterns forever after. The textbook gives you the vocabulary; the codebase gives you the muscle memory.

Code

Inventory cwkPippa's REST surface with ripgrep·bash
# Walk cwkPippa's REST surface yourself
cd ~/projects/cwkPippa

# Count routes per file (rough surface map)
rg -c '@router\.' backend/routes/ | sort -t: -k2 -n -r | head -20

# See every @router decorator (the verb + path map)
rg '@router\.(get|post|put|patch|delete)' backend/routes/ | head -40

# Find SSE endpoints (StreamingResponse usage)
rg 'StreamingResponse|text/event-stream' backend/

# Find 202 + Location patterns (async)
rg 'HTTP_202_ACCEPTED|status_code=202' backend/

# Inspect CORS allowlist
rg '_allowed_origins' backend/
Simplified excerpt: status codes, Location, idempotency in one file·python
# A snippet that demonstrates many patterns at once — from backend/routes/conversations.py
# (paraphrased / simplified; read the real file for the full version)
from fastapi import APIRouter, HTTPException, Response, status
from backend.store import conversations

router = APIRouter(prefix='/api/conversations', tags=['conversations'])

@router.get('/{cid}')
async def get_conversation(cid: str):
    # Healing runs on every GET — rebuild aborted turns + cleanup dangling parents
    convo = await conversations.heal_and_load(cid)
    if not convo:
        raise HTTPException(status.HTTP_404_NOT_FOUND, detail='conversation not found')
    return convo

@router.post('', status_code=status.HTTP_201_CREATED)
async def create_conversation(response: Response):
    new_id = await conversations.create()
    response.headers['Location'] = f'/api/conversations/{new_id}'
    return {'id': new_id, 'created_at': now_utc().isoformat()}

@router.put('/{cid}/title')
async def rename(cid: str, payload: dict):
    # Idempotent — same title twice = same state
    await conversations.update_title(cid, payload['title'])
    return {'id': cid, 'title': payload['title']}

@router.delete('/{cid}', status_code=status.HTTP_204_NO_CONTENT)
async def delete(cid: str):
    await conversations.delete(cid)
    # 204 — no body

External links

Exercise

If you have access to cwkPippa's repo (or any moderately-sized FastAPI codebase), walk these in order: (1) main.py — note the CORS allowlist and middleware order, (2) one resource router (conversations.py if cwkPippa) — note the verb + path patterns, (3) the chat route — find where the SSE happens. For each, identify ONE thing the codebase does that you wouldn't have predicted from the textbook (a deviation, pragmatism, or pattern you hadn't seen). Write down why you think it was done that way. Bonus: read the CLAUDE.md or repo README for the same files and see if your guess matches the documented reason.
Hint
The deviations from textbook REST are often the most instructive lines. cwkPippa's healing layer on GET is one (most APIs don't mutate state on GET; cwkPippa does, but for a specific reason). The lack of ETags is another. Finding these gaps and reasoning about them is the skill that separates 'I read the book' from 'I can ship a system.'

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.