Fastapi
npx machina-cli add skill muhammederem/chief/fastapi --openclawFiles (1)
SKILL.md
12.9 KB
FastAPI Framework
Overview
FastAPI is a modern, fast web framework for building APIs with Python based on standard Python type hints. It provides automatic validation, serialization, and interactive API documentation.
Installation
pip install fastapi uvicorn[standard]
pip install pydantic pydantic-settings
Basic Application
Hello World
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
Run Server
uvicorn main:app --reload
# Or with specific host/port
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
Project Structure
Recommended Structure
myapp/
├── app/
│ ├── __init__.py
│ ├── main.py # Application entry
│ ├── api/
│ │ ├── v1/
│ │ │ ├── __init__.py
│ │ │ ├── endpoints/ # API endpoints
│ │ │ └── router.py # API router
│ ├── core/
│ │ ├── config.py # Configuration
│ │ ├── security.py # Security
│ │ └── deps.py # Dependencies
│ ├── models/ # Database models
│ ├── schemas/ # Pydantic schemas
│ ├── services/ # Business logic
│ └── db/
│ └── database.py # Database connection
├── tests/
├── alembic.ini
└── requirements.txt
Main Application
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api.v1.router import api_router
app = FastAPI(
title="My API",
description="API description",
version="1.0.0"
)
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include routers
app.include_router(api_router, prefix="/api/v1")
Pydantic Schemas
Request/Response Models
from pydantic import BaseModel, EmailStr, Field
from typing import Optional, List
from datetime import datetime
class UserBase(BaseModel):
email: EmailStr
name: str = Field(..., min_length=1, max_length=100)
class UserCreate(UserBase):
password: str = Field(..., min_length=8)
class UserUpdate(BaseModel):
name: Optional[str] = None
email: Optional[EmailStr] = None
class UserResponse(UserBase):
id: int
created_at: datetime
is_active: bool
class Config:
from_attributes = True # For ORM models
Advanced Schemas
from enum import Enum
class Status(str, Enum):
ACTIVE = "active"
INACTIVE = "inactive"
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float = Field(..., gt=0)
tax: Optional[float] = None
tags: List[str] = []
status: Status = Status.INACTIVE
class Config:
json_schema_extra = {
"example": {
"name": "Item name",
"price": 10.5,
"tags": ["electronics", "tech"]
}
}
Dependency Injection
Dependencies
from fastapi import Depends, Header, HTTPException, status
async def get_db():
"""Database session dependency"""
db = SessionLocal()
try:
yield db
finally:
db.close()
async def get_current_user(
token: str = Header(...),
db: Session = Depends(get_db)
):
"""Get authenticated user"""
user = verify_token(token, db)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid credentials"
)
return user
# Use in endpoint
@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
return current_user
Class-based Dependencies
class CommonQueryParams:
def __init__(
self,
skip: int = 0,
limit: int = 100,
sort: str = "created_at"
):
self.skip = skip
self.limit = limit
self.sort = sort
@app.get("/items/")
async def read_items(commons: CommonQueryParams = Depends()):
return {"skip": commons.skip, "limit": commons.limit}
Database Integration
SQLAlchemy Setup
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = "postgresql://user:pass@localhost/db"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
Model Definition
from sqlalchemy import Column, Integer, String, Boolean, DateTime
from sqlalchemy.sql import func
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True, nullable=False)
name = Column(String, nullable=False)
hashed_password = Column(String, nullable=False)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
CRUD Operations
from typing import Generic, TypeVar, Type, List
from pydantic import BaseModel
ModelType = TypeVar("ModelType", bound=Base)
CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel)
UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel)
class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
def __init__(self, model: Type[ModelType]):
self.model = model
def get(self, db: Session, id: int) -> Optional[ModelType]:
return db.query(self.model).filter(self.model.id == id).first()
def get_multi(
self, db: Session, skip: int = 0, limit: int = 100
) -> List[ModelType]:
return db.query(self.model).offset(skip).limit(limit).all()
def create(
self, db: Session, obj_in: CreateSchemaType
) -> ModelType:
obj_in_data = obj_in.dict()
db_obj = self.model(**obj_in_data)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
def update(
self,
db: Session,
db_obj: ModelType,
obj_in: UpdateSchemaType
) -> ModelType:
obj_data = obj_in.dict(exclude_unset=True)
for field in obj_data:
setattr(db_obj, field, obj_data[field])
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj
def delete(self, db: Session, id: int) -> ModelType:
obj = db.query(self.model).get(id)
db.delete(obj)
db.commit()
return obj
Authentication
JWT Authentication
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
@app.post("/token")
async def login(
form_data: OAuth2PasswordRequestForm = Depends(),
db: Session = Depends(get_db)
):
user = authenticate_user(db, form_data.username, form_data.password)
if not user:
raise HTTPException(status_code=400, detail="Incorrect email or password")
access_token = create_access_token(data={"sub": user.email})
return {"access_token": access_token, "token_type": "bearer"}
Protected Routes
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
security = HTTPBearer()
async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security),
db: Session = Depends(get_db)
):
token = credentials.credentials
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
email: str = payload.get("sub")
except JWTError:
raise HTTPException(status_code=403, detail="Invalid token")
user = get_user_by_email(db, email=email)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
RESTful CRUD Endpoints
Complete CRUD
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
router = APIRouter(prefix="/items", tags=["items"])
@router.post("/", response_model=ItemResponse)
async def create_item(
item: ItemCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Create item"""
return crud.item.create(db, item)
@router.get("/", response_model=List[ItemResponse])
async def list_items(
skip: int = 0,
limit: int = 100,
db: Session = Depends(get_db)
):
"""List items with pagination"""
return crud.item.get_multi(db, skip=skip, limit=limit)
@router.get("/{item_id}", response_model=ItemResponse)
async def get_item(
item_id: int,
db: Session = Depends(get_db)
):
"""Get single item"""
item = crud.item.get(db, item_id)
if not item:
raise HTTPException(status_code=404, detail="Item not found")
return item
@router.put("/{item_id}", response_model=ItemResponse)
async def update_item(
item_id: int,
item_update: ItemUpdate,
db: Session = Depends(get_db)
):
"""Update item"""
item = crud.item.get(db, item_id)
if not item:
raise HTTPException(status_code=404, detail="Item not found")
return crud.item.update(db, item, item_update)
@router.delete("/{item_id}")
async def delete_item(
item_id: int,
db: Session = Depends(get_db)
):
"""Delete item"""
item = crud.item.get(db, item_id)
if not item:
raise HTTPException(status_code=404, detail="Item not found")
crud.item.delete(db, item_id)
return {"message": "Item deleted"}
Advanced Features
Background Tasks
from fastapi import BackgroundTasks
def send_email(email: str, message: str):
# Send email logic
pass
@router.post("/signup")
async def signup(
email: str,
background_tasks: BackgroundTasks
):
background_tasks.add_task(send_email, email, "Welcome!")
return {"message": "Signup successful"}
WebSockets
from fastapi import WebSocket
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Message received: {data}")
File Upload
from fastapi import UploadFile, File
@router.post("/upload")
async def upload_file(file: UploadFile = File(...)):
contents = await file.read()
# Save file
return {"filename": file.filename}
Exception Handlers
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
@app.exception_handler(ValueError)
async def value_error_handler(request: Request, exc: ValueError):
return JSONResponse(
status_code=400,
content={"detail": str(exc)}
)
Middleware
Custom Middleware
from fastapi import Request
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
Testing
Test Client
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_read_item():
response = client.get("/items/1")
assert response.status_code == 200
assert response.json()["id"] == 1
Best Practices
1. Project Structure
- Separate concerns (routes, models, services)
- Use dependency injection
- Keep business logic in services
2. Validation
- Use Pydantic for all input/output
- Define custom validators
- Provide examples in schemas
3. Error Handling
- Use appropriate HTTP status codes
- Provide clear error messages
- Handle exceptions globally
4. Performance
- Use async/await
- Optimize database queries
- Cache frequently accessed data
- Use pagination
5. Security
- Enable CORS properly
- Use HTTPS in production
- Validate and sanitize inputs
- Implement rate limiting
6. Documentation
- Use docstrings for endpoints
- Provide request/response examples
- Document authentication
Integration
- PostgreSQL: SQLAlchemy models
- MongoDB: Motor for async
- Redis: Caching and sessions
- Docker: Containerization
- AWS Lambda: Serverless deployment
Source
git clone https://github.com/muhammederem/chief/blob/main/.claude/skills/backend/fastapi/SKILL.mdView on GitHub Overview
FastAPI is a modern, fast web framework for building APIs with Python based on standard Python type hints. It provides automatic validation, serialization, and interactive API documentation.
How This Skill Works
You declare endpoints with Python type hints and use Pydantic models for request and response schemas. FastAPI automatically validates inputs, serializes outputs, and supports dependency injection for concerns like authentication and database access.
When to Use It
- Need a fast, standards-based Python API with automatic validation and interactive docs
- Build clear request/response models using Pydantic schemas
- Require dependency injection for permissions, security, or database sessions
- Organize a large app with versioned routers (e.g., /api/v1)
- Prototype or MVP quickly with minimal boilerplate
Quick Start
- Step 1: Install FastAPI and uvicorn (and pydantic if needed) via pip
- Step 2: Create a FastAPI app, define routes, and models, then add dependencies
- Step 3: Run the server with uvicorn main:app --reload
Best Practices
- Define Pydantic schemas for all request and response bodies
- Structure code with app, api/v1, core, models, and schemas as shown
- Use Depends to centralize shared logic (auth, DB sessions)
- Enable CORS thoughtfully in development and production
- Leverage FastAPI's automatic docs for testing and onboarding
Example Use Cases
- Hello World endpoint returning a JSON message
- Read item by ID from the path parameter (e.g., /items/{item_id})
- Run the server with uvicorn main:app --reload for development
- Configure a FastAPI app with CORSMiddleware and include routers under /api/v1
- Define and use Pydantic models for users and items, including enums and nested fields
Frequently Asked Questions
Add this skill to your agents