fastapi-api-patterns
fastapi-api-patterns
Instructions
This skill provides comprehensive REST API design patterns and implementation templates for FastAPI applications. It covers CRUD operations, pagination, filtering, request/response models, error handling, and API organization following modern best practices.
1. CRUD Endpoint Patterns
Create, Read, Update, Delete endpoints using FastAPI routers:
# Use CRUD template to generate complete endpoint set
cp ./skills/fastapi-api-patterns/templates/crud_endpoint.py app/routers/items.py
# Customize for your model
# - Replace Item model with your Pydantic model
# - Update database operations
# - Add authentication dependencies
What This Provides:
POST /items/- Create new itemGET /items/{item_id}- Read single item by IDGET /items/- Read multiple items with paginationPUT /items/{item_id}- Update entire itemPATCH /items/{item_id}- Partial updateDELETE /items/{item_id}- Delete item
Router Structure:
from fastapi import APIRouter, HTTPException, Depends, status
from typing import List
router = APIRouter(
prefix="/items",
tags=["items"],
responses={404: {"description": "Not found"}},
)
2. Pagination and Filtering
Implement pagination with query parameters:
# Use pagination template
cp ./skills/fastapi-api-patterns/templates/pagination.py app/utils/pagination.py
Pagination Strategies:
1. Offset-Based Pagination (Simple):
@router.get("/items/")
async def list_items(skip: int = 0, limit: int = 10):
return items[skip : skip + limit]
2. Cursor-Based Pagination (Performance):
@router.get("/items/")
async def list_items(cursor: str | None = None, limit: int = 10):
# Use last item ID as cursor for next page
# Better for large datasets
3. Page-Based Pagination (User-Friendly):
@router.get("/items/")
async def list_items(page: int = 1, page_size: int = 10):
skip = (page - 1) * page_size
return items[skip : skip + page_size]
Filtering Patterns:
@router.get("/items/")
async def list_items(
skip: int = 0,
limit: int = 10,
category: str | None = None,
min_price: float | None = None,
max_price: float | None = None,
search: str | None = None,
):
# Apply filters before pagination
filtered_items = apply_filters(items, category, min_price, max_price, search)
return filtered_items[skip : skip + limit]
Sorting:
from enum import Enum
class SortBy(str, Enum):
name = "name"
price = "price"
created_at = "created_at"
@router.get("/items/")
async def list_items(
sort_by: SortBy = SortBy.created_at,
order: Literal["asc", "desc"] = "desc",
):
# Sort before returning
3. Request and Response Models
Define clear Pydantic models for type safety and validation:
Base Models:
from pydantic import BaseModel, Field, validator
from datetime import datetime
class ItemBase(BaseModel):
"""Shared properties"""
name: str = Field(..., min_length=1, max_length=100)
description: str | None = Field(None, max_length=500)
price: float = Field(..., gt=0)
category: str
class ItemCreate(ItemBase):
"""Properties required for creation"""
pass
class ItemUpdate(BaseModel):
"""Properties that can be updated"""
name: str | None = None
description: str | None = None
price: float | None = Field(None, gt=0)
category: str | None = None
class ItemInDB(ItemBase):
"""Properties stored in database"""
id: int
created_at: datetime
updated_at: datetime
class Item(ItemInDB):
"""Properties returned to client"""
class Config:
from_attributes = True
Response Models with Metadata:
from typing import Generic, TypeVar, List
from pydantic import BaseModel
T = TypeVar('T')
class PaginatedResponse(BaseModel, Generic[T]):
items: List[T]
total: int
page: int
page_size: int
pages: int
@router.get("/items/", response_model=PaginatedResponse[Item])
async def list_items(page: int = 1, page_size: int = 10):
total = len(items)
pages = (total + page_size - 1) // page_size
skip = (page - 1) * page_size
return PaginatedResponse(
items=items[skip : skip + page_size],
total=total,
page=page,
page_size=page_size,
pages=pages,
)
4. Error Handling Strategies
Implement consistent error handling:
# Use error handling template
cp ./skills/fastapi-api-patterns/templates/error_handling.py app/utils/errors.py
HTTP Exception Patterns:
from fastapi import HTTPException, status
# 404 Not Found
if item is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Item with id {item_id} not found"
)
# 400 Bad Request
if price < 0:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Price must be positive"
)
# 409 Conflict
if item_exists:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Item with this name already exists"
)
# 403 Forbidden
if not is_owner:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not authorized to modify this item"
)
Custom Exception Handlers:
from fastapi import Request
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: Request, exc: ItemNotFoundError):
return JSONResponse(
status_code=404,
content={
"error": "not_found",
"message": f"Item {exc.item_id} not found",
"item_id": exc.item_id
}
)
Validation Error Customization:
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
return JSONResponse(
status_code=422,
content={
"error": "validation_error",
"message": "Invalid request data",
"details": exc.errors()
}
)
5. Dependency Injection for Common Logic
Use dependencies for authentication, database sessions, and validation:
from fastapi import Depends, Header, HTTPException
# Authentication dependency
async def verify_token(x_token: str = Header(...)):
if x_token != "secret-token":
raise HTTPException(status_code=401, detail="Invalid token")
return x_token
# Database session dependency
async def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# Pagination dependency
async def pagination_params(
skip: int = 0,
limit: int = 10,
max_limit: int = 100
):
if limit > max_limit:
limit = max_limit
return {"skip": skip, "limit": limit}
# Use in endpoints
@router.get("/items/")
async def list_items(
token: str = Depends(verify_token),
db: Session = Depends(get_db),
pagination: dict = Depends(pagination_params),
):
return db.query(Item).offset(pagination["skip"]).limit(pagination["limit"]).all()
6. API Router Organization
Structure APIs with APIRouter for modularity:
# app/routers/items.py
from fastapi import APIRouter
router = APIRouter(
prefix="/items",
tags=["items"],
dependencies=[Depends(verify_token)],
responses={404: {"description": "Not found"}},
)
# app/main.py
from fastapi import FastAPI
from app.routers import items, users
app = FastAPI()
app.include_router(items.router)
app.include_router(users.router, prefix="/api/v1")
7. OpenAPI Documentation Enhancement
Generate better API documentation:
# Generate enhanced OpenAPI docs
bash ./skills/fastapi-api-patterns/scripts/generate-openapi-docs.sh
Endpoint Documentation:
@router.post(
"/items/",
response_model=Item,
status_code=status.HTTP_201_CREATED,
summary="Create a new item",
description="Create a new item with the provided data",
response_description="The created item",
responses={
201: {"description": "Item created successfully"},
400: {"description": "Invalid input data"},
409: {"description": "Item already exists"},
}
)
async def create_item(item: ItemCreate):
"""
Create a new item with all the information:
- **name**: Item name (required, 1-100 characters)
- **description**: Item description (optional, max 500 characters)
- **price**: Item price (required, must be positive)
- **category**: Item category (required)
"""
pass
Examples
Example 1: Complete CRUD API for Chat Messages
# Copy chat API example
cp ./skills/fastapi-api-patterns/examples/chat_api.py app/routers/chat.py
Features:
- Create chat messages
- List messages with pagination and filtering
- Get single message by ID
- Update message content
- Delete messages
- Search messages by content
- Filter by user, channel, date range
Result: Production-ready chat message API with full CRUD operations
Example 2: User Management API
# Copy user management example
cp ./skills/fastapi-api-patterns/examples/user_management.py app/routers/users.py
Features:
- User registration with validation
- User authentication (simulated)
- Profile retrieval and updates
- Password change endpoint
- List users with role filtering
- User deactivation (soft delete)
Result: Complete user management system with security best practices
Example 3: Memory/Context Endpoints for AI Applications
# Copy memory endpoints example
cp ./skills/fastapi-api-patterns/examples/memory_endpoints.py app/routers/memory.py
Features:
- Store conversation context
- Retrieve context by session ID
- Update context with new messages
- Clear old contexts
- Search contexts by keywords
- Pagination for large context histories
Result: API for managing AI conversation memory and context
Requirements
Dependencies:
- FastAPI 0.100+
- Pydantic 2.0+
- Python 3.10+
Optional Dependencies:
- SQLAlchemy (for database operations)
- python-jose (for JWT authentication)
- passlib (for password hashing)
- python-multipart (for file uploads)
Project Structure:
app/
├── main.py
├── routers/
│ ├── __init__.py
│ ├── items.py
│ ├── users.py
│ └── chat.py
├── models/
│ ├── __init__.py
│ └── schemas.py
├── utils/
│ ├── pagination.py
│ └── errors.py
└── dependencies.py
Best Practices
1. Use Response Models:
- Always specify
response_modelto control what's returned - Use separate models for create, update, and read operations
- Never expose sensitive data (passwords, tokens)
2. Consistent Error Responses:
- Use standard HTTP status codes
- Return structured error objects with
error,message, anddetails - Include request ID for debugging
3. Pagination Everywhere:
- Never return unbounded lists
- Default to reasonable page sizes (10-50 items)
- Include total count and page metadata
4. Validation and Documentation:
- Use Pydantic Field validators for complex validation
- Document all endpoints with descriptions and examples
- Use response examples in OpenAPI schema
5. Dependencies for Reusability:
- Extract common logic into dependencies
- Use dependency injection for auth, DB, pagination
- Keep endpoints thin, move logic to services
6. Versioning:
- Use prefix-based versioning (
/api/v1/items) - Keep old versions running during migration
- Document breaking changes clearly
Validation Script
Validate endpoint structure and best practices:
# Validate all endpoints in a router file
bash ./skills/fastapi-api-patterns/scripts/validate-endpoints.sh app/routers/items.py
# What it checks:
# - Response models defined
# - Status codes specified
# - Error handling present
# - Documentation strings
# - Proper HTTP methods
# - Path parameter validation
Performance Considerations
Database Queries:
- Use pagination to limit query size
- Add indexes on frequently filtered fields
- Use database-level filtering, not Python filtering
- Implement query result caching for expensive operations
Response Size:
- Exclude unnecessary fields from responses
- Support field selection via query params
- Compress responses with gzip middleware
- Use streaming for large responses
Request Validation:
- Set reasonable limits on request sizes
- Validate early and fail fast
- Use background tasks for heavy processing
- Implement rate limiting on expensive endpoints
Plugin: fastapi-backend Version: 1.0.0 Category: API Development Skill Type: REST API Patterns