Kipt

CLAUDE.md for FastAPI: a copyable template for Python API projects

A CLAUDE.md for FastAPI you can copy: fastapi dev and uv commands, router layout, async def vs def rules, Annotated dependencies, tests and AGENTS.md.

By the Kipt team · Updated 24 October 2026

What should a CLAUDE.md for FastAPI include?

A CLAUDE.md for FastAPI should tell Claude Code how to install, run, test and lint the service, where the app object and routers live, and your rules for async def versus def, dependency injection, settings and response models. Add your gotchas and keep the file under 200 lines, per Anthropic's CLAUDE.md docs.

FastAPI leaves a lot of decisions to you: there is no required project layout, no built-in ORM and no single way to write dependencies. That freedom is exactly why agents drift in FastAPI code. One session writes async def endpoints that call a blocking driver, the next uses default-value Depends() while the rest of the code uses Annotated. A short CLAUDE.md that pins these choices is the fix.

The template below follows the current FastAPI docs, which recommend uv for project management and the fastapi CLI for running the app. PyPI lists FastAPI as requiring Python 3.10 or newer. Ruff, SQLModel and PostgreSQL are common picks rather than FastAPI requirements, so swap them for your own tools. For the general CLAUDE.md format, see the CLAUDE.md guide.

The FastAPI CLAUDE.md template

Save this as CLAUDE.md at the repo root next to pyproject.toml. Replace the angle-bracket placeholders and delete what does not fit.

# <Service name>

<One sentence: what this API does and who calls it.>
Stack: FastAPI, Python 3.12, Pydantic v2, <SQLModel on PostgreSQL>, uv.
App entrypoint: `app.main:app`, set in pyproject.toml under [tool.fastapi]

## Commands
- Install: `uv sync`
- Dev server: `uv run fastapi dev` (auto-reload, http://127.0.0.1:8000, docs at /docs)
- Production-style run: `uv run fastapi run` (no reload, listens on 0.0.0.0)
- Tests: `uv run pytest`; one test: `uv run pytest tests/test_items.py::test_create_item`
- Lint and format: `uv run ruff check .` then `uv run ruff format .`
- Add a package: `uv add <package>` (ask first)
- Done means: ruff and the tests for touched modules pass

## Project structure
- `app/main.py`: creates `app = FastAPI()` and includes the routers
- `app/routers/`: one `APIRouter` per resource (`items.py`, `users.py`)
- `app/dependencies.py`: shared dependencies (DB session, current user, settings)
- `app/schemas.py`: request and response models. `app/models.py`: table models
- `app/config.py`: `Settings(BaseSettings)` and a cached `get_settings()`
- `app/internal/`: admin and internal-only routes
- `tests/`: pytest tests that mirror `app/`
- Every package directory has an `__init__.py`

## Endpoints
- Request and response bodies are Pydantic models
- Every endpoint declares a return type or `response_model`
- Separate input and output models (`UserCreate` has a password, `UserPublic` never does)
- Client errors raise `HTTPException` with a specific status code and detail
- Routers use `APIRouter(prefix="/items", tags=["items"])`
- Path operation functions stay thin; business logic lives in `app/services/`

## async def or def
- `async def` only when every call inside is awaitable (async driver, `httpx.AsyncClient`)
- Blocking libraries (sync DB drivers, `requests`, file I/O) mean plain `def`;
  FastAPI runs those in a threadpool
- Never call blocking code inside an `async def` function
- When unsure, use plain `def`

## Dependencies and settings
- Inject with `Annotated[Type, Depends(func)]`, never as a default value
- Reuse aliases from dependencies.py: `SessionDep = Annotated[Session, Depends(get_session)]`
- Settings come from `pydantic-settings`, read once through `@lru_cache` on `get_settings()`
- Secrets come from environment variables or `.env` (git-ignored). Never hard-code or log them

## Testing
- `TestClient` from `fastapi.testclient` (needs httpx). Test functions are plain `def`
- Swap dependencies with `app.dependency_overrides[real] = fake`;
  reset with `app.dependency_overrides = {}` after each test
- Async helpers in tests: `@pytest.mark.anyio` plus `httpx.AsyncClient`
- `AsyncClient` does not run lifespan events; use asgi-lifespan's `LifespanManager` if needed
- Each endpoint gets a success test, a validation error test and an auth failure test
- Every bug fix comes with a test that failed before the fix

## Do not
- Do not start the server with raw uvicorn commands; use `fastapi dev` or `fastapi run`
- Do not change a response model without updating its tests
- Do not add a package without asking
- Do not commit `.env`

## Gotchas
- `fastapi dev` sets `FASTAPI_ENV=development` unless it is already set; `fastapi run` does not set it
- If `main.py` moves, update `[tool.fastapi] entrypoint` in pyproject.toml
- OpenAPI schema: http://127.0.0.1:8000/openapi.json
- <Your service's own surprises: external APIs, background workers, rate limits>

What does each part of the template do?

Header and entrypoint. The FastAPI CLI docs explain that fastapi tries to find an object called app in main.py, and recommend setting entrypoint under [tool.fastapi] in pyproject.toml so other tools find it too. Stating the entrypoint in CLAUDE.md means Claude never guesses the import path when it writes a run command or a test.

Commands. fastapi dev runs with auto-reload on 127.0.0.1, and fastapi run disables reload and listens on 0.0.0.0, as described in the CLI docs. The uv commands come from the FastAPI page on virtual environments: uv creates the environment for you, and uv run runs commands inside it. The single-test command matters most for an agent, because rerunning one test after an edit is fast and keeps the loop tight.

Project structure. The main.py, dependencies.py, routers/ and internal/ layout is the example structure from FastAPI's Bigger Applications tutorial, including an __init__.py in every directory. The schemas.py, services/ and config.py names are common choices, not FastAPI rules, so rename them to match your code.

Endpoints. FastAPI uses the return type or response_model to validate, document and "limit and filter" output data, which the response model docs call particularly important for security. Their example shows how one shared user model could send passwords to every client. Separate input and output models make that mistake hard to repeat.

async def or def. This section paraphrases the "In a hurry?" advice from FastAPI's concurrency page: use async def with libraries you call with await, use def for libraries that block, and "If you just don't know, use normal def." Plain def path operations run in an external threadpool, so blocking calls there do not stall the server. Blocking calls inside async def do, and that is a bug agents write often.

Dependencies and settings. FastAPI started recommending Annotated for dependencies in version 0.95.0, and the dependencies tutorial shows storing an Annotated type in a variable to reuse it. The settings pattern, BaseSettings from pydantic-settings plus @lru_cache so the settings object is created once, comes from the settings guide.

Testing. The testing tutorial uses TestClient, which needs httpx, with normal def test functions. Testing dependencies with overrides covers app.dependency_overrides and resetting it to an empty dict. The async test notes, including the warning that AsyncClient won't trigger lifespan events, come from the async tests page.

Gotchas. The FASTAPI_ENV behavior is documented on the CLI page. It trips up startup code that checks the environment, so it earns a line.

Which agent mistakes does the template prevent?

Each section maps to a mistake that is easy for an agent to make in a FastAPI codebase, because the framework allows several valid styles:

Without the rule Line in the template that prevents it
A blocking database call inside an async def endpoint stalls every request "Never call blocking code inside an async def function"
A new endpoint returns the table model, password hash included "Separate input and output models" and "declares a return type or response_model"
Half the code uses Depends() as a default value, half uses Annotated "Inject with Annotated[Type, Depends(func)]"
Tests hit the real database or real settings "Swap dependencies with app.dependency_overrides"
Settings are re-read from disk on every request "read once through @lru_cache on get_settings()"
Claude starts the app with a guessed import path The entrypoint line and uv run fastapi dev

When you add your own rules, use the same test: name the mistake first, then write the shortest line that prevents it. If you cannot name a mistake, the line probably does not belong in CLAUDE.md.

How do you adapt it to your project?

  • pip instead of uv: drop the uv run prefix and tell Claude to activate the virtualenv first, or it may run the wrong interpreter.
  • SQLAlchemy instead of SQLModel: change the stack line and model file names. If you use Alembic, add its migration commands and a "never edit an applied migration" rule.
  • Async database driver: flip the async rule so endpoints that use the database are async def, and say which session dependency to use.
  • Auth: name the dependency that returns the current user and the routes that must use it.
  • Large services: move router-specific rules into .claude/rules/routers.md with paths: ["app/routers/**/*.py"] so they load only when Claude edits a router.

After saving, run /context in a Claude Code session to confirm the file loaded. On an existing codebase, /init drafts a CLAUDE.md from what it finds, which you can merge with this template.

How do you reuse it as AGENTS.md?

If teammates use Codex or Cursor as well, put the template's content in AGENTS.md and keep CLAUDE.md as an import plus Claude-only notes. Claude Code reads AGENTS.md by itself only when there is no CLAUDE.md or CLAUDE.local.md (v2.1.277 or later), so the import keeps both files working together:

@AGENTS.md

## Claude Code only
- Use plan mode before changing auth dependencies or database models
- Run `/doctor prompt-audit` after editing AGENTS.md or this file

Claude reads the imported file first, then the rest. The AGENTS.md guide explains which tools read that file, and Claude Code memory covers how files in subfolders load on demand.

How do you keep the template accurate?

Add a rule when Claude repeats a mistake, and remove rules it follows without prompting. When you upgrade FastAPI or change tools, update the commands first, because a stale command fails every session. /doctor prompt-audit (Claude Code v2.1.283 or later) flags outdated or conflicting instructions. CLAUDE.md guides Claude but does not enforce anything, so use a hook for hard limits.

If you maintain several Python services, a reusable copy saves time. Kipt keeps prompts and Agent Skills as Markdown in a private GitHub repo you own; you could store this template there as a prompt with blanks like {{service_name:orders-api}} and fill it per service. Using Django for the web side? See the Django CLAUDE.md template. There are also Next.js and Rails versions.

Questions

What should a CLAUDE.md for FastAPI include?

Your install, run, test and lint commands, where the app object and routers live, when to use async def versus def, how dependencies and settings are injected, and how tests override dependencies. Those are the decisions Claude cannot reliably infer from a few files.

Should FastAPI endpoints in my project be async def?

FastAPI's docs say to use async def when you call libraries that need await, and plain def when a library blocks, such as most database drivers, because FastAPI runs def functions in a threadpool. If you are not sure, the docs say to use plain def. Write your project's choice into CLAUDE.md so Claude stays consistent.

How do I tell Claude Code to run my FastAPI app?

Write the command in CLAUDE.md, for example uv run fastapi dev, and set the app location in pyproject.toml under [tool.fastapi] with an entrypoint such as app.main:app. The FastAPI docs recommend the pyproject entry because other tools can find the app from it too.

How should Claude write tests for a FastAPI app?

With TestClient and pytest, using plain def test functions, and with app.dependency_overrides to swap real dependencies such as the database or settings for test versions. For async helpers inside tests, the docs show pytest.mark.anyio with an httpx AsyncClient.

Is this FastAPI CLAUDE.md template too long?

It is about 70 lines, well under the 200 lines Anthropic's memory docs suggest per file. If yours grows, move rules that apply to one area, such as routers or migrations, into .claude/rules/ files with a paths field so they load only when needed.

More in Templates

All guides

Keep your prompts and skills in one place, in your own GitHub. Install every skill with one command.

Get started

Kipt is an independent product, not affiliated with or endorsed by Anthropic, OpenAI, Cursor or GitHub. Claude, ChatGPT, Codex, Cursor and GitHub are trademarks of their owners.