Home / FastAPI AGENTS.md + Cursor rules
AGENTS.md and Cursor rules for FastAPI + SQLAlchemy + Pydantic
AI agents write FastAPI code that runs. The problem is what it quietly gets wrong:
- No ownership check.
GET /invoices/{id}fetches any invoice by ID. That is an IDOR, and the most common API bug there is. - Pydantic v1 habits:
class Config,.dict(),@validatorin a v2 codebase. - Legacy SQLAlchemy:
session.query(), lazy loads insideasync def, N+1 on every list endpoint. - Schema drift: models changed with no Alembic migration, or an autogenerated one committed without reading it.
- Tests that test nothing: SQLite instead of Postgres, mocked sessions, no 401/404 cases.
Where the rules go
AGENTS.md at the repo root holds the stack, layout, commands and non-negotiables. Codex, Cursor, Windsurf and others read it, and Claude Code picks it up through @AGENTS.md in CLAUDE.md. Path-scoped details go in .cursor/rules/*.mdc with globs, so the Alembic rule loads only when alembic/** or models are in context.
A layout that works
00-core (always on), 10-structure (app/**), 20-api-routes (app/api/**), 30-pydantic-schemas (app/schemas/**), 40-sqlalchemy-models (models, services), 50-alembic-migrations (alembic/**), 60-security (core, deps, auth routes), 70-testing (tests/**).
The three rules that matter most
- Authorization in the query:
select(Invoice).where(Invoice.id == id, Invoice.owner_id == user.id), with 404 for other users’ objects. - Migrations are reviewed code: autogenerate, then read and fix it; every migration has a working
downgrade()and is round-trip tested. - Every endpoint gets four tests: happy path, 422, 401, and 404 for another user, against a real Postgres in a rolled-back transaction.
Free snippet: copy it
--- description: FastAPI routes and dependencies globs: app/api/**/*.py,app/main.py alwaysApply: false --- - Every route: explicit response_model (or return annotation), status_code, and summary. POST create → 201, DELETE → 204 with no body. - Use Annotated dependencies: SessionDep = Annotated[AsyncSession, Depends(get_session)], CurrentUser = Annotated[User, Depends(get_current_user)]. - Any route touching user data depends on CurrentUser and passes user.id into the service; ownership is filtered in the query. Other users' objects → 404. - List endpoints are paginated (limit ≤ 100, default 20) and have a stable order_by. - Don't catch broad Exception in routes. No business logic in routes. - CORS: explicit allow_origins from Settings; never ["*"] together with allow_credentials=True.
FAQ
Does Cursor read AGENTS.md for a FastAPI project?
Yes. Cursor reads AGENTS.md at the repo root as well as .cursor/rules/*.mdc. Keep shared stack rules in AGENTS.md and path-scoped FastAPI, SQLAlchemy and Alembic details in .mdc rules with globs.
How do I stop AI agents from creating IDOR bugs in FastAPI?
Add a rule that every route touching user data depends on get_current_user and filters ownership in the query (owner_id == user.id), returning 404 for other users’ objects, plus a required test for the other-user case.
Should AI agents use Alembic autogenerate?
Yes, but the rule should require reading and fixing the generated file: autogenerate misses renames, some server defaults, enums and constraint changes. Require a working downgrade and an upgrade, downgrade -1, upgrade round trip.
How do I keep agents on Pydantic v2 and SQLAlchemy 2.0?
Say it explicitly in a scoped rule: ConfigDict, field_validator and model_dump instead of class Config, @validator and .dict(); Mapped[] with mapped_column and select() instead of session.query().
Should FastAPI tests use SQLite?
Not if production is Postgres. Use a real Postgres via Docker or testcontainers, run migrations once per session, and wrap each test in a transaction that rolls back.
Want the whole drawer?
The Python/FastAPI Backend Kit ($19) ships a fill-in AGENTS.md + CLAUDE.md, 8 scoped Cursor rules (structure, routes, Pydantic v2, SQLAlchemy 2, Alembic, security, testing), 10 prompts (/endpoint, /migration, /security-audit…) and definition-of-done + pre-deploy checklists.