Sub-Skill: FastAPI
Purpose: Prevent common FastAPI and Pydantic mistakes. Apply these rules whenever writing or reviewing FastAPI route handlers, Pydantic schemas, or async API code.
Rules
Always declare request and response bodies as Pydantic models — never use raw
dictorAnyfor I/O. Reference: ERR-2026-025Always use Pydantic v2 syntax (
model_config,model_dump,model_validate) unless the project explicitly pins Pydantic v1 (pydantic<2in requirements). Reference: ERR-2026-025Always declare route handlers as
async defunless the handler calls blocking I/O that cannot be awaited; userun_in_executorfor blocking calls inside async handlers.Always specify an explicit
response_modelon every route decorator so FastAPI filters and validates the response shape.Always use
status_codeon the route decorator (@app.post("/items", status_code=201)) rather than constructing aResponseobject just to set the status.Never raise bare
Exceptionin route handlers; raiseHTTPExceptionwith a meaningfulstatus_codeanddetailstring, or define a custom exception with an exception handler.Always register exception handlers via
@app.exception_handler(MyError)for domain errors rather than catching them inside every route.Use
Depends()for shared logic (auth, DB sessions, pagination params) — never duplicate the same logic across multiple route functions.Prefer
APIRouterwith aprefixandtagslist to group related endpoints; register routers withapp.include_router()rather than defining all routes on the app object.Always configure CORS via
CORSMiddlewarewith an explicitallow_originslist; never useallow_origins=["*"]in production.Use the
lifespancontext manager (FastAPI ≥ 0.93) for startup/shutdown logic instead of the deprecated@app.on_event("startup")/@app.on_event("shutdown")decorators.Always use
BackgroundTasks(injected viaDependsor as a route parameter) for fire-and-forget work; neverasyncio.create_taskinside a route without a lifecycle guard.Avoid exposing internal field names in OpenAPI by setting
model_config = ConfigDict(populate_by_name=True)and usingaliasoralias_generatorwhen the wire format differs from the Python attribute name.Use
pydantic-settings(BaseSettings) for configuration; never reados.environdirectly inside route handlers or dependency functions.Always test FastAPI routes with
TestClient(sync) orAsyncClientfromhttpx(async); never spin up a real server in unit tests.Ensure path operation functions declare only the parameters they actually use; unused
Requestinjections or staleDependsimports are dead code and confuse readers.Before adding a new route, check whether an existing router already owns that resource prefix; adding routes to the wrong router breaks OpenAPI tag grouping.
Never store mutable state in module-level variables inside route modules; use dependency-injected singletons or
app.stateinstead.
See also
skills/python/SKILL.mdskills/code-quality/SKILL.md