FastAPI
Quick Start
from fastapi import FastAPI
app = FastAPI()
@app.get("/health") async def health_check(): return {"status": "ok"}
@app.get("/users/{user_id}") async def get_user(user_id: int): return {"user_id": user_id}
Features
Feature Description Guide
Routing Path params, query params, body ROUTING.md
Pydantic Schemas, validation, serialization SCHEMAS.md
Dependencies Injection, database sessions DEPENDENCIES.md
Auth JWT, OAuth2, security utils AUTH.md
Database SQLAlchemy async, migrations DATABASE.md
Testing pytest, AsyncClient TESTING.md
Common Patterns
Pydantic Schemas
from pydantic import BaseModel, EmailStr, Field, field_validator
class UserCreate(BaseModel): email: EmailStr name: str = Field(..., min_length=2, max_length=100) password: str = Field(..., min_length=8)
@field_validator("password")
@classmethod
def validate_password(cls, v: str) -> str:
if not any(c.isupper() for c in v):
raise ValueError("Must contain uppercase")
if not any(c.isdigit() for c in v):
raise ValueError("Must contain digit")
return v
class UserResponse(BaseModel): model_config = ConfigDict(from_attributes=True)
id: UUID
email: EmailStr
name: str
created_at: datetime
Dependency Injection
from fastapi import Depends from sqlalchemy.ext.asyncio import AsyncSession
async def get_db() -> AsyncGenerator[AsyncSession, None]: async with async_session_maker() as session: yield session
async def get_current_user( token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db), ) -> User: payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"]) user = await db.get(User, payload["sub"]) if not user: raise HTTPException(status_code=401) return user
Type aliases for cleaner signatures
DB = Annotated[AsyncSession, Depends(get_db)] CurrentUser = Annotated[User, Depends(get_current_user)]
Route with Service Layer
@router.get("/", response_model=PaginatedResponse[UserResponse]) async def list_users( db: DB, current_user: CurrentUser, page: int = Query(1, ge=1), limit: int = Query(20, ge=1, le=100), ): service = UserService(db) users, total = await service.list(offset=(page - 1) * limit, limit=limit) return PaginatedResponse.create(data=users, total=total, page=page, limit=limit)
@router.post("/", response_model=UserResponse, status_code=201) async def create_user(db: DB, user_in: UserCreate): service = UserService(db) if await service.get_by_email(user_in.email): raise HTTPException(status_code=409, detail="Email exists") return await service.create(user_in)
Workflows
API Development
-
Define Pydantic schemas for request/response
-
Create service layer for business logic
-
Add route with dependency injection
-
Write tests with pytest-asyncio
-
Document with OpenAPI (automatic)
Service Pattern
class UserService: def init(self, db: AsyncSession): self.db = db
async def get_by_id(self, user_id: UUID) -> User | None:
result = await self.db.execute(
select(User).where(User.id == user_id)
)
return result.scalar_one_or_none()
async def create(self, data: UserCreate) -> User:
user = User(**data.model_dump(), hashed_password=hash_password(data.password))
self.db.add(user)
await self.db.commit()
return user
Best Practices
Do Avoid
Use async/await everywhere Sync operations in async code
Validate with Pydantic v2 Manual validation
Use dependency injection Direct imports
Handle errors with HTTPException Generic exceptions
Use type hints Any types
Project Structure
app/ ├── main.py ├── core/ │ ├── config.py │ ├── security.py │ └── deps.py ├── api/ │ └── v1/ │ ├── init.py │ ├── users.py │ └── auth.py ├── models/ ├── schemas/ ├── services/ └── db/ ├── base.py └── session.py tests/ ├── conftest.py └── test_users.py
For detailed examples and patterns, see reference files above.