Python FastAPI Setup
Overview
Create or modernize FastAPI services around a small repeatable structure:
app/main.py -> app/core/config.py -> app/api/v1/endpoints/ -> middleware -> tests -> deployment
Prefer direct startup commands, typed settings, explicit lifespan ownership for process-level resources, and a minimal test suite. Keep the scaffold boring on purpose: a future maintainer should be able to find the app, settings, routers, health check, and startup command without hunting through wrapper scripts.
Workflow
Classify the workspace before writing files.
- Treat the target as greenfield if it is empty or the user asks for a new service.
- Treat it as existing if it has Python source,
pyproject.toml, requirements.txt, an ASGI app, tests, Docker files, CI, or git history.
- For existing projects, inventory the current entrypoint, router layout, dependency manager, settings pattern, middleware, tests, deployment files, and startup commands before editing.
For greenfield scaffolds, run the bundled script from this skill directory:
python3 scripts/scaffold_fastapi_project.py \
--name inventory_api \
--out /path/to/inventory_api
- The scaffold always generates
gunicorn.conf.py, Docker, and Compose files for a production-ready default.
- The scaffold script renders reusable project and deployment configuration from
assets/ for pyproject.toml, Docker, Compose, and Gunicorn. Update those assets when the shared standard changes; update the Python script when generation logic, arguments, app files, or dependency policy change.
- Read
references/python-fastapi-blueprint.md before changing generated files or adding new script options.
For existing projects, patch conservatively.
- Preserve working endpoints and behavior.
- Before moving routes, record the existing endpoint contract: path, method, status code, response shape, sample payloads, headers, and any query/path parameters visible in the current code or tests.
- Add or update regression tests for those existing contracts before or during the refactor. The tests should assert the old payloads and status codes, not newly invented placeholder data.
- Move toward
app/main.py only when it improves clarity or matches the user's request.
- Retire wrapper launchers such as
run_service.py after replacing them with documented direct commands.
- Keep the repo's dependency manager unless there is a clear reason to change it.
- Add only the modules and settings the service actually uses.
- Do not replace legacy route payloads, IDs, names, or response field types with new sample fixtures just because the code moved into a cleaner module.
Keep app composition in one place.
- Export the ASGI app as
app = create_app() or app = FastAPI(...) from app/main.py.
- Register routers, middleware, and lifespan from
app/main.py.
- Use FastAPI lifespan for startup/shutdown resources such as connection pools, long-lived clients, thread pools, background processors, model handles, or service instances.
- Store process-level instances on
app.state and expose them through typed dependency accessors instead of making endpoints know raw app.state keys.
Centralize settings.
- Prefer
pydantic-settings with a small Settings class in app/core/config.py.
- Use an
lru_cache settings getter when settings are read repeatedly.
- Keep
.env.example safe: placeholders and local defaults only, no secrets.
Split routers predictably.
- Put route modules under
app/api/v1/endpoints/ for new services unless the repo already has a better convention.
- Keep
/health lightweight and available at the root path unless the existing service defines a different health contract.
- Add domain routers only when requested or clearly needed.
Choose deployment commands by environment.
- Local development:
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
- Local smoke test:
uvicorn app.main:app --host 0.0.0.0 --port 8000
- Production default: use Gunicorn with the external
uvicorn-worker package, for example gunicorn -c gunicorn.conf.py app.main:app.
- Container default: run the same Gunicorn command from
gunicorn.conf.py; let Kubernetes, Compose, or the platform replicate containers.
- Do not use
uvicorn.workers.UvicornWorker in new code; Uvicorn documents that module as deprecated.
Add tests and validation.
- Add at least one
TestClient test for /health.
- For existing projects, add regression tests for each migrated legacy endpoint whose behavior the user asked to preserve.
- Keep baseline tests free of network, database, or cloud dependencies.
- Run the strongest available checks and report skipped checks with the missing dependency.
Default Structure
Prefer this layout for greenfield services:
app/
__init__.py
main.py
core/
__init__.py
config.py
api/
__init__.py
v1/
__init__.py
router.py
dependencies.py
endpoints/
__init__.py
health.py
middleware/
__init__.py
request_context.py
tests/
test_health.py
pyproject.toml
gunicorn.conf.py
Dockerfile
docker-compose.yml
.dockerignore
.env.example
.gitignore
README.md
Add CI, database modules, schemas, services, or auth only when the user asks for them or the existing project already needs them.
File Standards
app/main.py
- Own app creation, lifespan, middleware, and router registration.
- Keep business logic out of the entrypoint.
- Use a
create_app() factory when tests or configuration need a fresh app instance.
app/core/config.py
- Own typed settings and environment loading.
- Keep settings flat until the service has enough domains to justify nesting.
- Ignore unknown environment keys so local
.env files can contain unrelated variables without breaking imports.
app/api/v1/dependencies.py
- Own FastAPI dependency functions and typed accessors.
- Read process-level services from
request.app.state.
- Avoid constructing expensive services inside endpoint functions.
app/middleware/
- Keep custom middleware focused.
- Include request ID middleware when traceability matters.
- Add CORS only for browser-facing APIs or when requested.
pyproject.toml
- Keep runtime dependencies separate from optional dev/prod extras.
- Include unpinned
fastapi, uvicorn[standard], pydantic-settings, gunicorn, and uvicorn-worker for the default scaffold so pip resolves current compatible releases.
Docker Files
- Build from an official Python slim image unless the repo has a stronger base-image standard.
- Use exec-form
CMD.
- Prefer one process per container.
- Add a health check against
/health.
Validation
Use the strongest checks that work locally:
python3 -m py_compile app/main.py app/core/config.py app/api/v1/router.py app/api/v1/endpoints/health.py
python3 -m pytest
uvicorn app.main:app --host 0.0.0.0 --port 8000
curl -fsS http://127.0.0.1:8000/health
gunicorn -c gunicorn.conf.py app.main:app
curl -fsS http://127.0.0.1:8000/health
If the user wants container verification, run:
docker build -t fastapi-smoke .
docker run --rm -p 8000:8000 fastapi-smoke
curl -fsS http://127.0.0.1:8000/health
Do not claim validation that did not run. If fastapi, uvicorn, pytest, Docker, or Gunicorn are missing, report the exact skipped command and the missing tool.
References
- Read
references/python-fastapi-blueprint.md when choosing layout, lifespan/dependency patterns, deployment commands, Docker behavior, or eval expectations.
- Read
scripts/scaffold_fastapi_project.py before changing generated files or adding script options.
- Read
assets/ before changing generated pyproject.toml, Docker, Compose, or Gunicorn configuration; treat those files as templates and keep service-specific values behind @PLACEHOLDER@ variables.
1---2name: python-fastapi-setup3description: FastAPI service setup: app/main.py, API routers, pydantic-settings, lifespan/app.state services, middleware, health checks, pytest/TestClient, Uvicorn, Gunicorn/uvicorn-worker, Docker/Compose. Use for scaffolding or modernizing Python API services; skip for Flask/Django-only, frontend-only, non-Python APIs, or Supabase-specific work.4---56# Python FastAPI Setup78## Overview910Create or modernize FastAPI services around a small repeatable structure:1112```text13app/main.py -> app/core/config.py -> app/api/v1/endpoints/ -> middleware -> tests -> deployment14```1516Prefer direct startup commands, typed settings, explicit lifespan ownership for process-level resources, and a minimal test suite. Keep the scaffold boring on purpose: a future maintainer should be able to find the app, settings, routers, health check, and startup command without hunting through wrapper scripts.1718## Workflow19201. Classify the workspace before writing files.21 - Treat the target as greenfield if it is empty or the user asks for a new service.22 - Treat it as existing if it has Python source, `pyproject.toml`, `requirements.txt`, an ASGI app, tests, Docker files, CI, or git history.23 - For existing projects, inventory the current entrypoint, router layout, dependency manager, settings pattern, middleware, tests, deployment files, and startup commands before editing.24252. For greenfield scaffolds, run the bundled script from this skill directory:2627```bash28python3 scripts/scaffold_fastapi_project.py \29 --name inventory_api \30 --out /path/to/inventory_api31```3233 - The scaffold always generates `gunicorn.conf.py`, Docker, and Compose files for a production-ready default.34 - The scaffold script renders reusable project and deployment configuration from `assets/` for `pyproject.toml`, Docker, Compose, and Gunicorn. Update those assets when the shared standard changes; update the Python script when generation logic, arguments, app files, or dependency policy change.35 - Read `references/python-fastapi-blueprint.md` before changing generated files or adding new script options.36373. For existing projects, patch conservatively.38 - Preserve working endpoints and behavior.39 - Before moving routes, record the existing endpoint contract: path, method, status code, response shape, sample payloads, headers, and any query/path parameters visible in the current code or tests.40 - Add or update regression tests for those existing contracts before or during the refactor. The tests should assert the old payloads and status codes, not newly invented placeholder data.41 - Move toward `app/main.py` only when it improves clarity or matches the user's request.42 - Retire wrapper launchers such as `run_service.py` after replacing them with documented direct commands.43 - Keep the repo's dependency manager unless there is a clear reason to change it.44 - Add only the modules and settings the service actually uses.45 - Do not replace legacy route payloads, IDs, names, or response field types with new sample fixtures just because the code moved into a cleaner module.46474. Keep app composition in one place.48 - Export the ASGI app as `app = create_app()` or `app = FastAPI(...)` from `app/main.py`.49 - Register routers, middleware, and lifespan from `app/main.py`.50 - Use FastAPI lifespan for startup/shutdown resources such as connection pools, long-lived clients, thread pools, background processors, model handles, or service instances.51 - Store process-level instances on `app.state` and expose them through typed dependency accessors instead of making endpoints know raw `app.state` keys.52535. Centralize settings.54 - Prefer `pydantic-settings` with a small `Settings` class in `app/core/config.py`.55 - Use an `lru_cache` settings getter when settings are read repeatedly.56 - Keep `.env.example` safe: placeholders and local defaults only, no secrets.57586. Split routers predictably.59 - Put route modules under `app/api/v1/endpoints/` for new services unless the repo already has a better convention.60 - Keep `/health` lightweight and available at the root path unless the existing service defines a different health contract.61 - Add domain routers only when requested or clearly needed.62637. Choose deployment commands by environment.64 - Local development: `uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload`65 - Local smoke test: `uvicorn app.main:app --host 0.0.0.0 --port 8000`66 - Production default: use Gunicorn with the external `uvicorn-worker` package, for example `gunicorn -c gunicorn.conf.py app.main:app`.67 - Container default: run the same Gunicorn command from `gunicorn.conf.py`; let Kubernetes, Compose, or the platform replicate containers.68 - Do not use `uvicorn.workers.UvicornWorker` in new code; Uvicorn documents that module as deprecated.69708. Add tests and validation.71 - Add at least one `TestClient` test for `/health`.72 - For existing projects, add regression tests for each migrated legacy endpoint whose behavior the user asked to preserve.73 - Keep baseline tests free of network, database, or cloud dependencies.74 - Run the strongest available checks and report skipped checks with the missing dependency.7576## Default Structure7778Prefer this layout for greenfield services:7980```text81app/82 __init__.py83 main.py84 core/85 __init__.py86 config.py87 api/88 __init__.py89 v1/90 __init__.py91 router.py92 dependencies.py93 endpoints/94 __init__.py95 health.py96 middleware/97 __init__.py98 request_context.py99tests/100 test_health.py101pyproject.toml102gunicorn.conf.py103Dockerfile104docker-compose.yml105.dockerignore106.env.example107.gitignore108README.md109```110111Add CI, database modules, schemas, services, or auth only when the user asks for them or the existing project already needs them.112113## File Standards114115### `app/main.py`116117- Own app creation, lifespan, middleware, and router registration.118- Keep business logic out of the entrypoint.119- Use a `create_app()` factory when tests or configuration need a fresh app instance.120121### `app/core/config.py`122123- Own typed settings and environment loading.124- Keep settings flat until the service has enough domains to justify nesting.125- Ignore unknown environment keys so local `.env` files can contain unrelated variables without breaking imports.126127### `app/api/v1/dependencies.py`128129- Own FastAPI dependency functions and typed accessors.130- Read process-level services from `request.app.state`.131- Avoid constructing expensive services inside endpoint functions.132133### `app/middleware/`134135- Keep custom middleware focused.136- Include request ID middleware when traceability matters.137- Add CORS only for browser-facing APIs or when requested.138139### `pyproject.toml`140141- Keep runtime dependencies separate from optional dev/prod extras.142- Include unpinned `fastapi`, `uvicorn[standard]`, `pydantic-settings`, `gunicorn`, and `uvicorn-worker` for the default scaffold so pip resolves current compatible releases.143144### Docker Files145146- Build from an official Python slim image unless the repo has a stronger base-image standard.147- Use exec-form `CMD`.148- Prefer one process per container.149- Add a health check against `/health`.150151## Validation152153Use the strongest checks that work locally:154155```bash156python3 -m py_compile app/main.py app/core/config.py app/api/v1/router.py app/api/v1/endpoints/health.py157python3 -m pytest158uvicorn app.main:app --host 0.0.0.0 --port 8000159curl -fsS http://127.0.0.1:8000/health160```161162```bash163gunicorn -c gunicorn.conf.py app.main:app164curl -fsS http://127.0.0.1:8000/health165```166167If the user wants container verification, run:168169```bash170docker build -t fastapi-smoke .171docker run --rm -p 8000:8000 fastapi-smoke172curl -fsS http://127.0.0.1:8000/health173```174175Do not claim validation that did not run. If `fastapi`, `uvicorn`, `pytest`, Docker, or Gunicorn are missing, report the exact skipped command and the missing tool.176177## References178179- Read `references/python-fastapi-blueprint.md` when choosing layout, lifespan/dependency patterns, deployment commands, Docker behavior, or eval expectations.180- Read `scripts/scaffold_fastapi_project.py` before changing generated files or adding script options.181- Read `assets/` before changing generated `pyproject.toml`, Docker, Compose, or Gunicorn configuration; treat those files as templates and keep service-specific values behind `@PLACEHOLDER@` variables.