FastAPI governance

# Architectural governance for AI-generated FastAPI code

FastAPI projects move fast — new routers, new dependencies, and new endpoints land in a single agent session. Mneme governs the architectural decisions in those repos before generation, so an AI coding agent can't quietly collapse your router / service / repository boundaries or re-litigate settled conventions. There is no bespoke FastAPI parser; the decisions are repo-native rules enforced at the file-write seam and in CI.

Python ecosystem · Repo-native rules

## Why governance matters in FastAPI

FastAPI is built around standard Python type hints, dependency injection, automatic validation, and generated OpenAPI documentation. That structure is exactly what makes a project legible — and exactly what an AI coding agent drifts away from when it optimizes for "make the endpoint work" over "respect the architecture." The framework gives you a clean place to put routers, services, repositories, and Pydantic schemas; nothing forces an agent to keep them there.

The faster agents generate API surface, the more often the same architectural decisions get re-decided per file: where business logic lives, how the database is reached, which dependencies guard a router, what shape a response takes. Mneme injects the project's settled decisions into the agent's context and checks the diff, so speed doesn't come at the cost of a coherent codebase.

## What Mneme can govern

Mneme governs the architectural decisions a FastAPI team has already made — expressed as repo-native rules in a decision corpus, not as a built-in FastAPI rule pack. Common surfaces:

- Layer boundaries — router, service, repository, and schema responsibilities stay separated.
- Thin route handlers — business logic stays out of the presentation layer.
- Dependency injection — shared resources arrive via `Depends` rather than ad hoc construction.
- Authentication and authorization — protected routers carry their auth dependencies consistently.
- Validation and serialization — request and response models stay consistent instead of returning raw dicts or ORM objects.
- Async conventions and module structure — the approved package layout and async/sync boundaries are preserved.
- API discipline — versioning and OpenAPI decisions are not silently broken.

Five concrete examples of what the governance layer catches in FastAPI repos under AI-assisted development:

### Business logic in a route handler

Architecture

Rule. Route handlers must stay thin. Pricing, tax, and inventory logic belong in service modules.

    # AI-generated in routers/checkout.py
    @router.post("/checkout")
    async def checkout(payload: CheckoutIn):
        subtotal = sum(i.price * i.qty for i in payload.items)
        tax = subtotal * 0.0875
        # inventory mutation, payment call, email send inline...
        return {"total": subtotal + tax}

logic in presentation layer missing service abstraction

### Database access inside the router

Layer boundary

Rule. Persistence goes through repository modules. No raw queries or sessions in route handlers.

    # AI-generated in routers/users.py
    @router.get("/users/{user_id}")
    async def get_user(user_id: int, db: Session = Depends(get_db)):
        return db.execute(
            text(f"SELECT * FROM users WHERE id = {user_id}")
        ).fetchone()

persistence in route layer repository bypass

### Ad hoc construction instead of dependency injection

Dependency injection

Rule. Clients and sessions arrive through `Depends`. No module-level singletons constructed in handlers.

    # AI-generated in routers/reports.py
    @router.get("/reports")
    async def reports():
        client = StorageClient(API_KEY)   # built inline, not injected
        engine = create_engine(DATABASE_URL)
        ...

bypassed DI container untestable handler

### Protected router missing its auth dependency

Security

Rule. Routers under `/admin` must declare the auth dependency. No unauthenticated admin surface.

    # AI-generated in routers/admin.py
    router = APIRouter(prefix="/admin")  # dependencies=[Depends(require_admin)] dropped

    @router.delete("/users/{user_id}")
    async def delete_user(user_id: int):
        ...

missing auth dependency access-control regression

### Inconsistent response serialization

Schema discipline

Rule. Endpoints declare a `response_model`. No returning ORM objects or untyped dicts.

    # AI-generated in routers/orders.py
    @router.get("/orders/{order_id}")  # response_model omitted
    async def get_order(order_id: int):
        return order_repo.get(order_id)  # leaks the ORM model + internal fields

unbounded response shape schema contract drift

## Example governance decisions

These are illustrative rules a FastAPI team would author in its own decision corpus. Mneme does not ship them as defaults — it enforces the decisions you record.

decision · api-001 Architecture

Route handlers must remain thin. Business logic belongs in service modules.

An agent that inlines pricing, tax, or orchestration logic into a handler is flagged before the change lands.

decision · api-002 Layer boundary

Database access must go through repository modules rather than route handlers.

Sessions and queries that appear directly in a router are surfaced as a boundary violation.

decision · api-003 Security

Authentication dependencies must be applied consistently to protected routers.

Dropping the auth dependency on an admin or account router is treated as an access-control regression.

## How governance fits the workflow

Mneme sits between your architectural decisions and the AI coding tools writing FastAPI code. The loop is the same one Mneme runs for any repository:

1. #### Decisions and ADRs are recorded

   Your architectural decisions live as records in `.mneme/project_memory.json` — each with a scope, constraints, and anti-patterns. ADRs can be imported into the same corpus.

2. #### Mneme retrieves the relevant intent

   On a prompt or a file write, Mneme retrieves the decisions in scope for the change and injects them into the agent's context — governance before generation, not review after the fact.

3. #### The AI coding agent changes the code

   Claude Code, Cursor, Copilot, or a custom agent edits routers, services, repositories, and schemas with that context in hand.

4. #### Mneme checks the proposed change

   `mneme check` evaluates the diff against the corpus: a matched constraint surfaces as `WARN`, an anti-pattern hit as `FAIL`. Aligned changes pass cleanly.

5. #### CI gates the merge

   In `--mode strict` a `WARN` exits 1 and a `FAIL` exits 2, so the regression blocks the PR; `--mode warn` annotates without blocking. One corpus serves the editor, the terminal agent, and the CI runner.

## Where it fits in a Python codebase

FastAPI governance is Python governance applied to an API-shaped repository. The decision corpus lives at `.mneme/project_memory.json`; hooks fire on file writes; `mneme check --mode strict` gates PR diffs in CI. Because Mneme is itself implemented in Python, the broader Python governance surface — native CLI, benchmark coverage, Claude Code hooks — applies directly to FastAPI projects.

See the Claude Code integration and the Cursor integration for editor and terminal setup, the GitHub Actions integration for CI gating, and architectural drift for the problem this prevents.

Parent language Python governance → The full Python surface FastAPI projects inherit: native CLI, hooks, and benchmark coverage. Concept Governance before generation → Why decisions are injected into context, not just reviewed after the agent writes. Use case Coding assistant governance → End-to-end workflow with Claude Code, Cursor, and GitHub Actions on one corpus. Full matrix Coverage across all ecosystems → Canonical doc with capabilities, limitations, and roadmap per language and framework.