Backend City: Day 01: Laying a Real API Foundation (FastAPI, Tooling, Structure)

CTRL+STRUM // BUILDER LOG

Backend City: Day 01: Laying a Real API Foundation (FastAPI, Tooling, Structure)

Posted on December 30, 2025

CTRL+STRUM • BACKEND CITY • DAY 01
Backend City — Day 01: Pouring the Foundation

Day 01 is not glamorous. There are no fireworks. No “look mom I built OAuth.” This is the day we pour concrete and put up scaffolding — so the city doesn’t turn into a haunted parking garage later.

// fastapi
// api versioning
// config
// tooling
// tests
// docs

Why Day 01 matters

In production systems, the “boring stuff” is what keeps your service alive: clean structure, predictable configuration, health checks for load balancers, and tooling so every run/test/format action is identical across machines.

The goal is simple: make future changes boring. Boring means repeatable. Repeatable means survivable.

What we built (the concrete pour)

  • Project structure with clear separation (app/, tests/, docs/)
  • Router-based FastAPI layout so endpoints scale without chaos
  • Health endpoint: GET /api/v1/health returns {"status":"ok"}
  • API versioning baked in via /api/v1
  • Typed settings using pydantic-settings (env-first, .env for local)
  • CORS configured for local dev frontends (Vite/Next/etc.)
  • Lifespan startup logging (no deprecated on_event)
  • .env.example so setup is obvious and reproducible
  • Makefile runbook: run, run2, kill, test, fmt
  • Ruff formatting/linting configured via pyproject.toml
  • Pytest scaffolding with a first health-check test
  • Auto-generated API docs: Swagger UI at /docs + schema at /openapi.json

The core ideas (Backend City translation)

1) Routers = districts

If everything lives in one file, your “city” becomes a junk drawer. Routers let you build districts: users, auth, billing, etc. Teams can own a district without stepping on each other.

2) Health checks = the city heartbeat

Load balancers, deploy pipelines, and orchestrators all ask one question: “Are you alive and ready?” That’s what /health answers. It starts simple — and later grows into readiness/liveness checks and dependency verification.

3) Settings = zoning laws for environments

Dev, staging, prod — same code, different rules. Typed config prevents “works on my machine” from becoming “outage in prod.” Env vars for production, .env for local, and validation at startup.

4) CORS = letting the right cars into the city

During development, your frontend is usually on a different port. Browsers will block requests unless you explicitly allow them. Local dev gets a wide allowlist. Production gets strict origins only.

5) Tooling = the foreman on the construction site

A Makefile isn’t “extra.” It’s how you make commands consistent: one way to run, one way to test, one way to format. When a team grows, this stops friction from becoming culture debt.

6) /docs = the city’s public map

Swagger UI is your “show me what this API actually does” page. It proves your routes are registered, your schemas are valid, and your versioning prefix is working — without anyone needing to read the code first.

Pro move: when you add endpoints, check /docs immediately. If it’s not there, it’s not real.

How to run & test (the “don’t guess” section)

Format + lint

make fmt

Run tests

make test

Run server (default 8000)

make run

Run server on another port

make run PORT=8002
# or
make run2   # runs 8001

Kill whatever is holding your port hostage

make kill
# or
make kill PORT=8002

Verify health

curl http://127.0.0.1:8000/api/v1/health

Verify docs + schema

open http://127.0.0.1:8000/docs
curl http://127.0.0.1:8000/openapi.json
If you see Address already in use, that’s not failure — it just means something is still running on that port. Use make kill or run on a different port.

What tripped me up

FastAPI’s on_event startup pattern throws a deprecation warning now. Switching to lifespan handlers is the correct move — and it’s exactly why Day 01 exists: fix the foundation while it’s still cheap.

How this scales (real-world)

  • Health checks grow into readiness/liveness and dependency status
  • Routers become team-owned modules or split into services cleanly
  • Settings become secrets-aware config pipelines (Vault/SSM/etc.)
  • Tooling becomes CI gates (make test + make fmt)
  • Tests become the permission slip for refactors
  • API versioning prevents client breakage as the system evolves
  • /docs becomes your living contract (and the fastest sanity check in the stack)

Next up

Day 01 is infrastructure. Day 02 is where we start enforcing rules: request schemas, validation, and the early “building codes” that keep APIs clean.

~ ben.tankersley ⌁ echo "foundation poured."
~ ben.tankersley ⌁
> Last note sent by Ben Tankersley