$npx -y skills add sordi-ai/skill-everything --skill fastapiApply when building FastAPI endpoints, Pydantic models, or async APIs. Covers routing, dependency injection, error handling, testing, and OpenAPI hygiene.
| 1 | # Sub-Skill: FastAPI |
| 2 | |
| 3 | **Purpose:** Prevent common FastAPI and Pydantic mistakes. Apply these rules whenever writing or reviewing FastAPI route handlers, Pydantic schemas, or async API code. |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## Rules |
| 8 | |
| 9 | 1. **Always** declare request and response bodies as Pydantic models — never use raw `dict` or `Any` for I/O. Reference: ERR-2026-025 |
| 10 | |
| 11 | 2. **Always** use Pydantic v2 syntax (`model_config`, `model_dump`, `model_validate`) unless the project explicitly pins Pydantic v1 (`pydantic<2` in requirements). Reference: ERR-2026-025 |
| 12 | |
| 13 | 3. **Always** declare route handlers as `async def` unless the handler calls blocking I/O that cannot be awaited; use `run_in_executor` for blocking calls inside async handlers. |
| 14 | |
| 15 | 4. **Always** specify an explicit `response_model` on every route decorator so FastAPI filters and validates the response shape. |
| 16 | |
| 17 | 5. **Always** use `status_code` on the route decorator (`@app.post("/items", status_code=201)`) rather than constructing a `Response` object just to set the status. |
| 18 | |
| 19 | 6. **Never** raise bare `Exception` in route handlers; raise `HTTPException` with a meaningful `status_code` and `detail` string, or define a custom exception with an exception handler. |
| 20 | |
| 21 | 7. **Always** register exception handlers via `@app.exception_handler(MyError)` for domain errors rather than catching them inside every route. |
| 22 | |
| 23 | 8. **Use** `Depends()` for shared logic (auth, DB sessions, pagination params) — never duplicate the same logic across multiple route functions. |
| 24 | |
| 25 | 9. **Prefer** `APIRouter` with a `prefix` and `tags` list to group related endpoints; register routers with `app.include_router()` rather than defining all routes on the app object. |
| 26 | |
| 27 | 10. **Always** configure CORS via `CORSMiddleware` with an explicit `allow_origins` list; never use `allow_origins=["*"]` in production. |
| 28 | |
| 29 | 11. **Use** the `lifespan` context manager (FastAPI ≥ 0.93) for startup/shutdown logic instead of the deprecated `@app.on_event("startup")` / `@app.on_event("shutdown")` decorators. |
| 30 | |
| 31 | 12. **Always** use `BackgroundTasks` (injected via `Depends` or as a route parameter) for fire-and-forget work; never `asyncio.create_task` inside a route without a lifecycle guard. |
| 32 | |
| 33 | 13. **Avoid** exposing internal field names in OpenAPI by setting `model_config = ConfigDict(populate_by_name=True)` and using `alias` or `alias_generator` when the wire format differs from the Python attribute name. |
| 34 | |
| 35 | 14. **Use** `pydantic-settings` (`BaseSettings`) for configuration; never read `os.environ` directly inside route handlers or dependency functions. |
| 36 | |
| 37 | 15. **Always** test FastAPI routes with `TestClient` (sync) or `AsyncClient` from `httpx` (async); never spin up a real server in unit tests. |
| 38 | |
| 39 | 16. **Ensure** path operation functions declare only the parameters they actually use; unused `Request` injections or stale `Depends` imports are dead code and confuse readers. |
| 40 | |
| 41 | 17. **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. |
| 42 | |
| 43 | 18. **Never** store mutable state in module-level variables inside route modules; use dependency-injected singletons or `app.state` instead. |
| 44 | |
| 45 | --- |
| 46 | |
| 47 | ## See also |
| 48 | |
| 49 | - `skills/python/SKILL.md` |
| 50 | - `skills/code-quality/SKILL.md` |