app-scaffolding
npx skills add https://github.com/ingpdw/pdw-python-dev-tool --skill app-scaffolding
Agent 安装分布
Skill 文档
FastAPI Skill
Overview
FastAPI is a modern, high-performance async Python web framework for building APIs. It leverages Python type hints and Pydantic for automatic validation, serialization, and OpenAPI documentation generation. FastAPI runs on ASGI servers (Uvicorn, Hypercorn) and provides first-class support for async/await, dependency injection, and WebSockets.
Key characteristics:
- Automatic interactive API docs (Swagger UI at
/docs, ReDoc at/redoc) - Type-driven request validation and response serialization via Pydantic
- Native async support with full sync fallback
- Dependency injection system for shared logic and resource management
- Standards-based: OpenAPI 3.1, JSON Schema
Project Structure
Organize FastAPI projects with a clear separation of concerns:
project/
âââ app/
â âââ __init__.py
â âââ main.py # Application factory and lifespan
â âââ config.py # Settings via pydantic-settings
â âââ dependencies.py # Shared dependencies
â âââ models.py # SQLAlchemy / ORM models
â âââ schemas.py # Pydantic request/response schemas
â âââ routers/
â â âââ __init__.py # Router registration
â â âââ users.py
â â âââ items.py
â â âââ health.py
â âââ services/ # Business logic layer
â âââ middleware/ # Custom middleware
â âââ exceptions.py # Custom exception handlers
âââ tests/
â âââ conftest.py
â âââ test_users.py
â âââ test_items.py
âââ alembic/ # Database migrations
âââ pyproject.toml
âââ .env
See assets/app-template/ for a ready-to-use scaffold.
Application Factory and Lifespan
Use the async context manager lifespan pattern (the on_event decorator is deprecated):
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup: initialize resources
app.state.db_pool = await create_db_pool()
app.state.http_client = httpx.AsyncClient()
yield
# Shutdown: release resources
await app.state.http_client.aclose()
await app.state.db_pool.close()
def create_app() -> FastAPI:
app = FastAPI(
title="My API",
version="0.1.0",
lifespan=lifespan,
)
app.include_router(users_router)
app.include_router(items_router)
return app
app = create_app()
Route Definition
Define routes with HTTP method decorators. Use type-annotated parameters for automatic validation and documentation.
Path Parameters
@app.get("/users/{user_id}")
async def get_user(user_id: int) -> User:
...
Query Parameters
@app.get("/items")
async def list_items(skip: int = 0, limit: int = 20, q: str | None = None):
...
Request Body
from pydantic import BaseModel
class ItemCreate(BaseModel):
name: str
price: float
description: str | None = None
@app.post("/items", status_code=201)
async def create_item(item: ItemCreate) -> Item:
...
Multiple Parameter Sources
@app.put("/items/{item_id}")
async def update_item(
item_id: int, # path
item: ItemUpdate, # body
q: str | None = None, # query
x_token: Annotated[str, Header()], # header
):
...
Response Models
Control response shape and status codes:
@app.post("/users", response_model=UserOut, status_code=201)
async def create_user(user: UserCreate):
...
@app.get("/report", response_class=HTMLResponse)
async def get_report():
return "<html>...</html>"
Use response_model_exclude_unset=True to omit fields not explicitly set, and
response_model_exclude={"password"} to strip sensitive fields.
Router Organization
Split endpoints into routers for modularity:
# app/routers/users.py
from fastapi import APIRouter
router = APIRouter(prefix="/users", tags=["users"])
@router.get("/")
async def list_users():
...
@router.get("/{user_id}")
async def get_user(user_id: int):
...
Register routers in the application factory:
from app.routers import users, items
app.include_router(users.router)
app.include_router(items.router, prefix="/api/v1")
Apply shared dependencies to all routes on a router:
router = APIRouter(
prefix="/admin",
tags=["admin"],
dependencies=[Depends(require_admin)],
)
Dependency Injection
Use Depends() to declare reusable, composable dependencies. Dependencies can be
functions, async functions, or callable classes.
from fastapi import Depends
from typing import Annotated
async def get_db():
async with async_session() as session:
yield session
DbSession = Annotated[AsyncSession, Depends(get_db)]
async def get_current_user(db: DbSession, token: str = Depends(oauth2_scheme)) -> User:
...
CurrentUser = Annotated[User, Depends(get_current_user)]
@app.get("/me")
async def read_me(user: CurrentUser):
return user
Yield dependencies execute cleanup after the response is sent, making them ideal for database sessions, file handles, and other resources that require teardown.
See references/dependency-injection.md for advanced patterns including parameterized
dependencies, class-based dependencies, and testing overrides.
Request and Response Models with Pydantic
Define strict, validated schemas using Pydantic models. Separate input and output schemas to control what is accepted vs. returned:
class UserBase(BaseModel):
email: EmailStr
name: str
class UserCreate(UserBase):
password: str
class UserOut(UserBase):
id: int
created_at: datetime
model_config = ConfigDict(from_attributes=True)
Cross-reference: see the pydantic skill for comprehensive model patterns, custom validators, and serialization options.
Lifespan Events
The lifespan async context manager replaces the deprecated @app.on_event("startup")
and @app.on_event("shutdown") decorators. Everything before yield runs at startup;
everything after runs at shutdown.
Store shared resources on app.state so dependencies can access them:
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.redis = await aioredis.from_url("redis://localhost")
yield
await app.state.redis.close()
Background Tasks
Enqueue lightweight work to run after the response is returned:
from fastapi import BackgroundTasks
def send_notification(email: str, message: str):
# blocking or async work
...
@app.post("/orders")
async def create_order(order: OrderCreate, background_tasks: BackgroundTasks):
result = await process_order(order)
background_tasks.add_task(send_notification, order.email, "Order confirmed")
return result
For heavier workloads, offload to a task queue (Celery, ARQ, or Dramatiq) instead.
Error Handling
HTTPException
from fastapi import HTTPException
@app.get("/items/{item_id}")
async def get_item(item_id: int):
item = await fetch_item(item_id)
if not item:
raise HTTPException(status_code=404, detail="Item not found")
return item
Custom Exception Handlers
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
class ItemNotFoundError(Exception):
def __init__(self, item_id: int):
self.item_id = item_id
@app.exception_handler(ItemNotFoundError)
async def item_not_found_handler(request, exc):
return JSONResponse(
status_code=404,
content={"detail": f"Item {exc.item_id} not found"},
)
@app.exception_handler(RequestValidationError)
async def validation_handler(request, exc):
return JSONResponse(
status_code=422,
content={"detail": exc.errors(), "body": exc.body},
)
CORS Configuration
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://example.com"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Never use allow_origins=["*"] with allow_credentials=True in production. See
references/middleware.md for detailed CORS guidance and other middleware patterns.
Testing
Use TestClient for synchronous tests or httpx.AsyncClient for async tests.
Override dependencies to inject mocks:
from fastapi.testclient import TestClient
def test_read_items():
app.dependency_overrides[get_db] = lambda: mock_db
client = TestClient(app)
response = client.get("/items")
assert response.status_code == 200
app.dependency_overrides.clear()
Async test with httpx:
import pytest
from httpx import ASGITransport, AsyncClient
@pytest.mark.anyio
async def test_create_item():
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as client:
response = await client.post("/items", json={"name": "Widget", "price": 9.99})
assert response.status_code == 201
Further Reading
references/routing-patterns.md— advanced routing, file uploads, WebSockets, SSEreferences/middleware.md— middleware patterns, ordering, custom middlewarereferences/dependency-injection.md— DI composition, testing, scopingassets/app-template/— production-ready project scaffold
Cross-references:
- pydantic skill — model definitions, validators, serialization
- async-patterns skill — async/await best practices, concurrency
- uvicorn skill — ASGI server configuration and deployment