Skip to content

Latest commit

 

History

39 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fastapi-starter-kit

CI Python 3.12 Ruff License: MIT

A production-ready FastAPI starter with SQLite (sqlite3), full CRUD for users and items, Pydantic v2 schemas, CORS, structured logging, dotenv config, a pytest suite, Docker support, and an optional Cloudflare Worker/D1 edge shim.


Contents


Architecture

flowchart TD
    Req[HTTP Request] --> Main[main.py: CORS + exception handler]
    Main --> RouterL[Routers: users / items]
    RouterL -->|Pydantic v2 validation| Crud[crud.py]
    Crud -->|sqlite3 parameterized SQL| DB[(SQLite app.db / :memory:)]
Loading

Each HTTP request passes through the following layers:

HTTP request
     │
     ▼
 main.py ── CORS middleware
     │   └─ global exception handler (→ 500 JSON)
     ▼
 Router  ── Pydantic input validation (→ 422 on failure)
     │       routers/users.py  |  routers/items.py
     ▼
 crud.py ── sqlite3 parameterized queries
     │
     ▼
 SQLite  ── app.db (file) or :memory: (tests)

Key design decisions:

  • sqlite3, no ORM — persistence uses the Python standard library. Schema lives in database.py; queries in crud.py are parameterized (? placeholders).
  • Flat module layoutconfig, database, schemas, and crud are top-level modules; routers live in routers/. No unnecessary nesting.
  • Dependency injectionget_db is a FastAPI dependency that yields a sqlite3.Connection. Tests override it with a shared in-memory database.
  • No ORM objects in responses — handlers return dicts that FastAPI validates against Pydantic response models.
  • Cascade deletes — deleting a User deletes their Item rows via ON DELETE CASCADE (foreign keys are enabled with PRAGMA foreign_keys = ON).
  • Isolate mesh controls — L1 LRU cache and a 50k req/min limiter sit on the origin. Eight-PoP throughput figures in /ops/status are a configured catalog, not live Cloudflare Analytics.

Project structure

fastapi-starter-kit/
├── main.py              # App entry: CORS, logging, routers, error handler
├── config.py            # Env config (python-dotenv)
├── database.py          # sqlite3 connect, schema, get_db
├── schemas.py           # Pydantic schemas: Create / Update / Response
├── crud.py              # Parameterized SQL for users and items
├── cache.py             # In-isolate L1 LRU (TTL)
├── rate_limit.py        # Per-client sliding window
├── mesh.py              # PoP / tenant / LLM failover catalog
├── wrangler.toml        # Cloudflare Worker + D1 bindings
├── cloudflare/
│   ├── worker.js        # Edge proxy + geo shard headers
│   └── migrations/      # D1 SQL (same schema as sqlite3)
├── routers/
│   ├── users.py         # /users endpoints
│   ├── items.py         # /items endpoints
│   └── ops.py           # /ops mesh catalog endpoints
├── tests/
│   ├── conftest.py      # Fixtures: in-memory DB, client, seeded data
│   ├── test_health.py
│   ├── test_users.py
│   ├── test_items.py
│   ├── test_database.py
│   └── test_ops.py
├── .env.example         # Copy to .env before first run
├── .github/
│   └── workflows/
│       └── ci.yml       # CI: ruff lint+format, pytest, Docker build
├── Dockerfile
├── docker-compose.yml
├── pytest.ini
└── requirements.txt

Local setup

Requirements: Python 3.12+

# 1. Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate

# 2. Install dependencies
pip install -r requirements.txt

# 3. Configure environment
cp .env.example .env             # edit values as needed

# 4. Start the dev server (auto-reload on file changes)
uvicorn main:app --reload

The server starts at http://localhost:8000. The SQLite database (app.db) is created automatically on the first request.

Interactive docs:

URL Interface
http://localhost:8000/docs Swagger UI
http://localhost:8000/redoc ReDoc

Docker setup

Requirements: Docker 24+ with the Compose plugin

# 1. Configure environment
cp .env.example .env             # edit values as needed

# 2. Build and start
docker compose up --build

# 3. Stop and remove containers (data volume is preserved)
docker compose down

The app is available at http://localhost:8000. The SQLite database is stored in a named Docker volume (db-data) mounted at /app/data inside the container, so data persists across container restarts and rebuilds.

To wipe the database volume:

docker compose down -v

Healthcheck — Docker polls GET /health every 30 s (3 retries, 10 s start period). The container is marked healthy once the endpoint returns 200.


Environment variables

Copy .env.example to .env and adjust as needed. All variables have defaults so the app starts without a .env file.

Variable Default Description
SQLITE_PATH ./app.db Filesystem path to the SQLite database file
DATABASE_URL (unset) Optional alias; sqlite:///./app.db is mapped to a file path
ALLOWED_ORIGINS http://localhost:3000 Comma-separated list of CORS origins
RATE_LIMIT_PER_MINUTE 50000 Isolate rate limit per client IP (or CF-Connecting-IP)
L1_CACHE_MAXSIZE 4096 In-process LRU entries for GET /users/{id} and /items/{id}
L1_CACHE_TTL_SECONDS 30 L1 cache TTL
LOG_LEVEL INFO Logging verbosity: DEBUG INFO WARNING ERROR

API reference

Health

GET /health

HTTP/1.1 200 OK

{"status": "ok"}

GET /ops/status

Isolate cache/rate-limit stats plus the configured 8-shard topology. Topology figures are catalog values, not live Cloudflare Analytics.

GET /ops/route?country=JP

Maps ISO country code to D1 shard / colo (JPshard_apac_01 / NRT). Unknown countries fall back to shard_amer_01.


Users

GET /users/

Returns a paginated list of users.

Query param Default Description
skip 0 Records to skip
limit 100 Max records to return
HTTP/1.1 200 OK

[
  {
    "id": 1,
    "name": "Alice",
    "email": "alice@example.com",
    "is_active": true,
    "created_at": "2026-04-10T12:00:00Z"
  }
]

GET /users/{id}

HTTP/1.1 200 OK        → user object
HTTP/1.1 404 Not Found → {"detail": "User not found"}

POST /users/

{"name": "Alice", "email": "alice@example.com"}
HTTP/1.1 201 Created   → user object
HTTP/1.1 409 Conflict  → {"detail": "Email already registered"}
HTTP/1.1 422           → validation error detail

PUT /users/{id}

All fields are optional. Only supplied fields are updated.

{"name": "Alicia", "email": "alicia@example.com", "is_active": false}
HTTP/1.1 200 OK        → updated user object
HTTP/1.1 404 Not Found → {"detail": "User not found"}

DELETE /users/{id}

Cascades — also deletes all items owned by this user.

HTTP/1.1 204 No Content
HTTP/1.1 404 Not Found → {"detail": "User not found"}

Items

GET /items/

Query param Default Description
skip 0 Records to skip
limit 100 Max records to return
HTTP/1.1 200 OK

[
  {
    "id": 1,
    "title": "Widget",
    "description": "A fine widget",
    "owner_id": 1
  }
]

GET /items/{id}

HTTP/1.1 200 OK        → item object
HTTP/1.1 404 Not Found → {"detail": "Item not found"}

POST /items/

owner_id must reference an existing user.

{"title": "Widget", "description": "A fine widget", "owner_id": 1}
HTTP/1.1 201 Created   → item object
HTTP/1.1 404 Not Found → {"detail": "Owner user not found"}
HTTP/1.1 422           → validation error detail

PUT /items/{id}

All fields are optional. Pass "description": null to clear it.

{"title": "Updated Widget", "description": null}
HTTP/1.1 200 OK        → updated item object
HTTP/1.1 404 Not Found → {"detail": "Item not found"}

DELETE /items/{id}

Does not affect the owning user.

HTTP/1.1 204 No Content
HTTP/1.1 404 Not Found → {"detail": "Item not found"}

Running tests

pytest -v

The test suite uses a shared in-memory SQLite database (sqlite3 URI mode=memory&cache=shared) and httpx.AsyncClient — no running server or external services required. Each test function starts with a clean database.

pytest -v -k "TestCreateUser"    # run a single class
pytest -v --tb=short             # compact tracebacks

To run lint and format checks locally (same checks as CI):

ruff check .
ruff format --check .

Contributing

See CONTRIBUTING.md for the development workflow, code style guide, and pull request process.

For security issues, see SECURITY.md.

About

Production-ready FastAPI boilerplate with JWT auth, SQLAlchemy, and Docker

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages