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.
- 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 item
-
GET /items/{item_id}
-
Read single item by ID
-
GET /items/
-
Read multiple items with pagination
-
PUT /items/{item_id}
-
Update entire item
-
PATCH /items/{item_id}
-
Partial update
-
DELETE /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"}}, )
- 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:
- Offset-Based Pagination (Simple):
@router.get("/items/") async def list_items(skip: int = 0, limit: int = 10): return items[skip : skip + limit]
- 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
- 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
- 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() } )
- 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()
- 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")
- 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
- Use Response Models:
-
Always specify response_model to control what's returned
-
Use separate models for create, update, and read operations
-
Never expose sensitive data (passwords, tokens)
- Consistent Error Responses:
-
Use standard HTTP status codes
-
Return structured error objects with error , message , and details
-
Include request ID for debugging
- Pagination Everywhere:
-
Never return unbounded lists
-
Default to reasonable page sizes (10-50 items)
-
Include total count and page metadata
- Validation and Documentation:
-
Use Pydantic Field validators for complex validation
-
Document all endpoints with descriptions and examples
-
Use response examples in OpenAPI schema
- Dependencies for Reusability:
-
Extract common logic into dependencies
-
Use dependency injection for auth, DB, pagination
-
Keep endpoints thin, move logic to services
- 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