fastapi-best-practices
SkillDev toolsFastAPI patterns for high-performance async APIs
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the fastapi-best-practices skill
What this skill tells your AI
The instructions your AI receives, as published by baekenough/oh-my-customcode in .claude/skills/fastapi-best-practices/SKILL.md and read by ahel’s review.
Purpose
Apply FastAPI patterns for building high-performance async APIs.
Rules
1. Project Structure
structure:
domain_based: true
module_contents:
- router.py: API endpoints
- schemas.py: Pydantic models
- models.py: Database models
- service.py: Business logic
- dependencies.py: Route validators
- constants.py: Module constants
- config.py: Module configuration
- exceptions.py: Custom exceptions
- utils.py: Helper functions
imports:
style: explicit
example: "from src.auth import constants as auth_constants"
layout: |
src/
├── auth/
│ ├── router.py
│ ├── schemas.py
│ ├── models.py
│ ├── service.py
│ └── dependencies.py
├── users/
│ └── ...
├── config.py
└── main.py
2. Async Patterns
io_intensive:
use: "async def"
await: "asyncio.sleep(), httpx, asyncpg, etc."
example: |
async def fetch_data():
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.json()
sync_io:
use: "def (regular function)"
reason: FastAPI offloads to threadpool automatically
example: |
def read_file():
with open('file.txt') as f:
return f.read()
cpu_intensive:
avoid: async and threadpool
use: separate worker processes
example: "Use Celery or multiprocessing"
never:
- "time.sleep() in async functions"
- "Blocking I/O in async functions"
3. Pydantic Models
validation:
use_builtin: regex, enums, email, URL validators
custom_validators: for complex logic
base_model:
create_custom: true
purpose: enforce application-wide standards
example: |
from pydantic import BaseModel
from datetime import datetime
class AppBaseModel(BaseModel):
class Config:
from_attributes = True
json_encoders = {
datetime: lambda v: v.isoformat()
}
settings:
split: across modules
avoid: single global configuration
example: |
# auth/config.py
class AuthSettings(BaseSettings):
jwt_secret: str
jwt_algorithm: str = "HS256"
# database/config.py
class DatabaseSettings(BaseSettings):
url: str
pool_size: int = 5
4. Dependencies
usage:
- Dependency injection
- Validation against database
- Authentication/authorization
- Request scoped caching
patterns:
chain: avoid code repetition
cache: FastAPI caches within request scope
decouple: small, reusable functions
example: |
async def get_current_user(
token: str = Depends(oauth2_scheme),
db: Session = Depends(get_db)
) -> User:
user = await db.get_user_by_token(token)
if not user:
raise HTTPException(status_code=401)
return user
async def get_active_user(
user: User = Depends(get_current_user)
) -> User:
if not user.is_active:
raise HTTPException(status_code=403)
return user
5. Error Handling
custom_exceptions:
scope: module-specific
purpose: clarity and consistency
pattern: |
# auth/exceptions.py
class AuthException(Exception):
pass
class InvalidCredentials(AuthException):
pass
class TokenExpired(AuthException):
pass
# auth/router.py
@router.post("/login")
async def login(credentials: LoginSchema):
try:
return await auth_service.login(credentials)
except InvalidCredentials:
raise HTTPException(
status_code=401,
detail="Invalid credentials"
)
exception_handlers: |
@app.exception_handler(AuthException)
async def auth_exception_handler(request, exc):
return JSONResponse(
status_code=401,
content={"detail": str(exc)}
)
6. Database
naming:
establish: upfront conventions
consistency: across all models
migrations:
tool: Alembic
naming: explicit naming rules
design:
approach: SQL-first
then: add Pydantic models
patterns: |
# Use async database drivers
from sqlalchemy.ext.asyncio import AsyncSession
async def get_user(db: AsyncSession, user_id: int):
result = await db.execute(
select(User).where(User.id == user_id)
)
return result.scalar_one_or_none()
7. API Design
routing:
prefix: meaningful module prefix
tags: for documentation grouping
responses:
schema: always define response models
status_codes: document all possible codes
example: |
router = APIRouter(
prefix="/users",
tags=["users"]
)
@router.get(
"/{user_id}",
response_model=UserResponse,
responses={
404: {"model": ErrorResponse}
}
)
async def get_user(user_id: int):
...
8. Testing
setup:
async_client: from day one
structure: mirror module layout
patterns: |
import pytest
from httpx import AsyncClient
@pytest.fixture
async def client():
async with AsyncClient(app=app, base_url="http://test") as ac:
yield ac
@pytest.mark.asyncio
async def test_get_user(client):
response = await client.get("/users/1")
assert response.status_code == 200
Application
When writing FastAPI code:
- Always use domain-based project structure
- Always use
async deffor I/O operations - Prefer Pydantic for all validation
- Use dependencies for reusable logic
- Define module-specific exceptions
- Document API with response models
- Test with async client
- Use Ruff for code quality
Signals
- GitHub stars
- 34
- Forks
- 6
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
fastapi-best-practices- Source
- github.com/baekenough/oh-my-customcode