12 條 Cursor 規則避免 AI 助手寫出過時 FastAPI 程式碼
12 Cursor rules that stop AI assistants writing outdated FastAPI code
作者分享 12 條 Cursor 專案規則,以 `.cursor/rules/` 中按檔案 glob 載入的 `.mdc` 檔,約束 AI 助手採用較新的 FastAPI 慣例。
AI coding assistants still reach for FastAPI patterns from a few years ago: .dict(), orm_mode, session.query(), HTTPException raised deep inside service code, SQLite in tests. The code runs, but it isn't the code you'd write today.
Cursor's project rules fix most of this. A rule is a small .mdc file in .cursor/rules/ with a glob, so it loads only when you edit matching files:
---
description: "FastAPI routers, dependencies and Pydantic v2 schemas"
globs: src/app/**/router.py,src/app/**/schemas.py,src/app/**/deps.py
alwaysApply: false
---
- One APIRouter(prefix="/<plural-resource>") per feature ...
These are the 12 conventions that made the biggest difference for me.
Routers and schemas
-
Pydantic v2 APIs only.
model_validate,model_dump,ConfigDict,field_validator. Never.dict(),parse_obj,orm_mode,class Configor the v1validatordecorator. - Routers are thin and async. Parse, call a service, return a schema. No SQL or business rules in routers.
-
Always declare
response_model(or a return annotation) and an explicitstatus_code: 201 for create, 204 for delete. -
Schemas are named
XCreate/XUpdate/XRead. Input models useConfigDict(extra="forbid")so a sneaky"is_admin": trueis rejected. Apply updates withmodel_dump(exclude_unset=True).
Database
-
SQLAlchemy 2.0 style only.
Mapped[...],mapped_column(),select(). Neversession.query()ordeclarative_base(). -
Services own the transaction with
async with session.begin():. Repositories never commit. Useasync_sessionmaker(expire_on_commit=False). -
Relationships default to
lazy="raise". Load explicitly withselectinload()orjoinedload(), so there's no hidden IO in async code. -
Set
MetaData(naming_convention=...)on your Base so Alembic constraint names are deterministic. Never edit a migration that's already applied.
Errors, config and safety
-
Services raise your own
AppErrorsubclasses, neverHTTPException. One exception handler maps them to{"error": {"code": ..., "message": ...}}. -
Settings only through a cached
get_settings()built on pydantic-settings. Never reados.environelsewhere, and keep secrets asSecretStr. -
Never build SQL with f-strings. Use bound parameters or
text(...).bindparams().
Tests
-
pytest + pytest-asyncio +
httpx.AsyncClient(transport=ASGITransport(app=app)), against a real Postgres with a rollback per test. Clearapp.dependency_overridesafter each test. No SQLite stand-in.
Using them
Drop rules like these into .cursor/rules/, one file per area, with globs that match your layout. For Claude Code, the same conventions work as a short CLAUDE.md at the repo root.
Disclosure: I made a FastAPI rules pack with these conventions written out as 7 scoped .mdc files plus a CLAUDE.md, and I sell it for $12 at codebaserules.com. Two of the files are a free download, no email needed. Everything above works without buying anything. I'd like to hear which of these you'd change.
來源:DEV Community · dev.to