Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Design RESTful and GraphQL APIs following industry best practices for consistency, versioning, and developer experience.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
assets/rest-api-template.py
1"""2Production-ready REST API template using FastAPI.3Includes pagination, filtering, error handling, and best practices.4"""56from fastapi import FastAPI, HTTPException, Query, Path, Depends, status7from fastapi.middleware.cors import CORSMiddleware8from fastapi.middleware.trustedhost import TrustedHostMiddleware9from fastapi.responses import JSONResponse10from pydantic import BaseModel, Field, EmailStr, ConfigDict11from typing import Optional, List, Any12from datetime import datetime13from enum import Enum1415app = FastAPI(16title="API Template",17version="1.0.0",18docs_url="/api/docs"19)2021# Security Middleware22# Trusted Host: Prevents HTTP Host Header attacks23app.add_middleware(24TrustedHostMiddleware,25allowed_hosts=["*"] # TODO: Configure this in production, e.g. ["api.example.com"]26)2728# CORS: Configures Cross-Origin Resource Sharing29app.add_middleware(30CORSMiddleware,31allow_origins=["*"], # TODO: Update this with specific origins in production32allow_credentials=False, # TODO: Set to True if you need cookies/auth headers, but restrict origins33allow_methods=["*"],34allow_headers=["*"],35)3637# Models38class UserStatus(str, Enum):39ACTIVE = "active"40INACTIVE = "inactive"41SUSPENDED = "suspended"4243class UserBase(BaseModel):44email: EmailStr45name: str = Field(..., min_length=1, max_length=100)46status: UserStatus = UserStatus.ACTIVE4748class UserCreate(UserBase):49password: str = Field(..., min_length=8)5051class UserUpdate(BaseModel):52email: Optional[EmailStr] = None53name: Optional[str] = Field(None, min_length=1, max_length=100)54status: Optional[UserStatus] = None5556class User(UserBase):57id: str58created_at: datetime59updated_at: datetime6061model_config = ConfigDict(from_attributes=True)6263# Pagination64class PaginationParams(BaseModel):65page: int = Field(1, ge=1)66page_size: int = Field(20, ge=1, le=100)6768class PaginatedResponse(BaseModel):69items: List[Any]70total: int71page: int72page_size: int73pages: int7475# Error handling76class ErrorDetail(BaseModel):77field: Optional[str] = None78message: str79code: str8081class ErrorResponse(BaseModel):82error: str83message: str84details: Optional[List[ErrorDetail]] = None8586@app.exception_handler(HTTPException)87async def http_exception_handler(request, exc):88return JSONResponse(89status_code=exc.status_code,90content=ErrorResponse(91error=exc.__class__.__name__,92message=exc.detail if isinstance(exc.detail, str) else exc.detail.get("message", "Error"),93details=exc.detail.get("details") if isinstance(exc.detail, dict) else None94).model_dump()95)9697# Endpoints98@app.get("/api/users", response_model=PaginatedResponse, tags=["Users"])99async def list_users(100page: int = Query(1, ge=1),101page_size: int = Query(20, ge=1, le=100),102status: Optional[UserStatus] = Query(None),103search: Optional[str] = Query(None)104):105"""List users with pagination and filtering."""106# Mock implementation107total = 100108items = [109User(110id=str(i),111email=f"user{i}@example.com",112name=f"User {i}",113status=UserStatus.ACTIVE,114created_at=datetime.now(),115updated_at=datetime.now()116).model_dump()117for i in range((page-1)*page_size, min(page*page_size, total))118]119120return PaginatedResponse(121items=items,122total=total,123page=page,124page_size=page_size,125pages=(total + page_size - 1) // page_size126)127128@app.post("/api/users", response_model=User, status_code=status.HTTP_201_CREATED, tags=["Users"])129async def create_user(user: UserCreate):130"""Create a new user."""131# Mock implementation132return User(133id="123",134email=user.email,135name=user.name,136status=user.status,137created_at=datetime.now(),138updated_at=datetime.now()139)140141@app.get("/api/users/{user_id}", response_model=User, tags=["Users"])142async def get_user(user_id: str = Path(..., description="User ID")):143"""Get user by ID."""144# Mock: Check if exists145if user_id == "999":146raise HTTPException(147status_code=status.HTTP_404_NOT_FOUND,148detail={"message": "User not found", "details": {"id": user_id}}149)150151return User(152id=user_id,153email="[email protected]",154name="User Name",155status=UserStatus.ACTIVE,156created_at=datetime.now(),157updated_at=datetime.now()158)159160@app.patch("/api/users/{user_id}", response_model=User, tags=["Users"])161async def update_user(user_id: str, update: UserUpdate):162"""Partially update user."""163# Validate user exists164existing = await get_user(user_id)165166# Apply updates167update_data = update.model_dump(exclude_unset=True)168for field, value in update_data.items():169setattr(existing, field, value)170171existing.updated_at = datetime.now()172return existing173174@app.delete("/api/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT, tags=["Users"])175async def delete_user(user_id: str):176"""Delete user."""177await get_user(user_id) # Verify exists178return None179180if __name__ == "__main__":181import uvicorn182uvicorn.run(app, host="0.0.0.0", port=8000)183