agentleFS
AlteriomAlteriom/ai-dev-skills/skills/fastapi/SKILL.md

fastapi

Skill0 starsChanged 3 months agoMITView on GitHub

Build production-ready Python APIs with FastAPI using type hints, async/await, dependency injection, and automatic OpenAPI documentation

  • Installs packages

27 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use react, python and 2 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • VercelSkill

    react

    React renderer for json-render that turns JSON specs into React components. Use when working with @json-render/react, building React UIs from JSON, creating component catalogs, or rendering AI-generated specs.

    Also in 3 projects that use this

  • rogue-dev-studioSkill

    python

    Python 3.13 with uv package manager via mise. Use when the project needs this capability or the user / team manifest asks for it. Use for specialized `python` work when listed in TEAM.yaml or explicitly requested.

    Also in 3 projects that use this

  • BlockSkill

    plan

    Draft a TDD, PRD, or lightweight plan in the conversation without committing it.

    Also in 3 projects that use this

  • BlockSkill

    frontend-design

    Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.

    Also in 3 projects that use this

The file

We split this skill into the rules it gives an agent

8 rules. 0 are common, 5 are distinctive, and 3 are very specific to this project.

  • 0

    Common

    Most of the 25 edited versions we read keep these.

  • 5

    Distinctive

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use this skill when:

    line 26
  2. Distinctive:

    Don't use: when you need:

    line 36
  3. Very specific:

    Karpathy Principle: Simplicity First: - Start with a single main.py file.

    line 84
  4. Distinctive:

    Karpathy Principle: When NOT to Use: - Don't use FastAPI for CPU-intensive batch jobs (use Celery).

    line 86
  5. Very specific:

    Karpathy Principle: Goal-Driven Execution: - After setting up dependencies, test them in isolation: curl http://localhost:8000/me should …

    line 184
  6. Very specific:

    Karpathy Principle: Security By Design: - Default Pydantic validation stops 80% of attacks.

    line 186
  7. Distinctive:

    Karpathy Principle: Surgical Changes: - Add middleware (CORS, auth, logging) one at a time.

    line 369
  8. Distinctive:

    Before deploying FastAPI applications:

    line 473

“Common” is measured: kept by at least 6 in 10 of the 25 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

FastAPI - Modern Python API Framework

When to Use

Use this skill when:

  • Building REST APIs in Python with automatic validation
  • Need async/await support for high concurrency
  • Want automatic OpenAPI (Swagger) documentation
  • Type safety and editor autocompletion matter
  • Rapid prototyping with production-quality code
  • Integrating with modern Python tools (Pydantic, SQLAlchemy 2.0)
  • WebSocket or Server-Sent Events (SSE) support needed

Don't use when you need:

  • Long-established ecosystem (Flask/Django have more libraries)
  • Template rendering as primary feature (use Django)
  • Python 2.7 support (FastAPI requires Python 3.7+)
  • Zero learning curve (FastAPI concepts require understanding async/type hints)

Karpathy Principle: Think Before Coding - FastAPI's async support is powerful but easy to misuse. If you mix sync database drivers in async endpoints, you'll block the entire event loop. Plan your architecture before writing code.

Karpathy Principle: Trade-offs Everywhere - FastAPI's async runtime means: use async def for I/O-bound (DB, API calls), use def for CPU-bound (image processing). Mixing them wrong blocks the event loop.

Prerequisites

Required Knowledge

  • Python 3.9+ (type hints, async/await)
  • REST API concepts (HTTP methods, status codes, headers)
  • Pydantic basics (validation, models)
  • Async programming fundamentals

Required Tools

# Install FastAPI
pip install fastapi[all]  # Includes uvicorn, pydantic, etc.

# Or minimal install
pip install fastapi uvicorn[standard] pydantic

# For async database
pip install sqlalchemy[asyncio] asyncpg  # PostgreSQL
pip install sqlalchemy[asyncio] aiomysql  # MySQL

# Development tools
pip install pytest httpx pytest-asyncio

Project Structure

app/
├── main.py              # FastAPI app
├── models.py            # Pydantic models
├── database.py          # Database setup
├── routers/
│   ├── __init__.py
│   ├── users.py
│   └── posts.py
├── dependencies.py      # Shared dependencies
└── config.py            # Settings

Karpathy Principle: Simplicity First - Start with a single main.py file. Split into routers and modules only when you have 10+ endpoints. Premature organization adds complexity.

Karpathy Principle: When NOT to Use - Don't use FastAPI for CPU-intensive batch jobs (use Celery). Don't use it for WebSockets at scale (use dedicated WS server). Async doesn't fix CPU bottlenecks.

Core Workflows

1. Basic Application Setup

Simple API:

# main.py
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI(title="My API", version="1.0.0")

class Item(BaseModel):
    name: str
    price: float
    is_available: bool = True

@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None):
    return {"item_id": item_id, "q": q}

@app.post("/items", status_code=201)
async def create_item(item: Item):
    return item

# Run with: uvicorn main:app --reload

With Lifespan (startup/shutdown):

from contextlib import asynccontextmanager
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup
    app.state.db_engine = create_async_engine("postgresql+asyncpg://...")
    yield
    # Shutdown
    await app.state.db_engine.dispose()

app = FastAPI(lifespan=lifespan)

2. Dependency Injection

Database Dependency:

# database.py
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker

engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

async def get_db():
    async with AsyncSessionLocal() as session:
        yield session

# Usage in endpoint
from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession

@app.get("/users/{user_id}")
async def read_user(user_id: int, db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user

Authentication Dependency:

from fastapi import Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

security = HTTPBearer()

async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
    token = credentials.credentials
    # Verify token (JWT, etc.)
    user = await verify_token(token)
    if not user:
        raise HTTPException(status_code=401, detail="Invalid token")
    return user

@app.get("/me")
async def read_current_user(user = Depends(get_current_user)):
    return user

Karpathy Principle: Goal-Driven Execution - After setting up dependencies, test them in isolation: curl http://localhost:8000/me should return 401 without a token, not 500. Verify error handling works before building on it.

Karpathy Principle: Security By Design - Default Pydantic validation stops 80% of attacks. But enable CORS carefully (allow_origins=["*"] = security disaster). Validate file uploads (size, type, content).

3. Request Validation with Pydantic

Input Validation:

from pydantic import BaseModel, Field, EmailStr, validator
from typing import Annotated

class UserCreate(BaseModel):
    email: EmailStr
    password: Annotated[str, Field(min_length=8)]
    age: Annotated[int, Field(ge=18, le=120)]
    
    @validator('password')
    def password_strength(cls, v):
        if not any(c.isupper() for c in v):
            raise ValueError('Password must contain uppercase letter')
        return v

@app.post("/users")
async def create_user(user: UserCreate):
    # user is validated automatically
    return {"email": user.email, "age": user.age}

Query Parameters with Validation:

from typing import Annotated
from fastapi import Query

@app.get("/items")
async def list_items(
    skip: Annotated[int, Query(ge=0)] = 0,
    limit: Annotated[int, Query(ge=1, le=100)] = 10,
    search: Annotated[str | None, Query(max_length=50)] = None,
):
    return {"skip": skip, "limit": limit, "search": search}

4. Error Handling

Custom Exceptions:

from fastapi import HTTPException, 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={"detail": f"Item {exc.item_id} not found"},
    )

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    item = await db.get(item_id)
    if not item:
        raise ItemNotFoundError(item_id)
    return item

Validation Error Handling:

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={"detail": exc.errors(), "body": exc.body},
    )

Common Patterns

1. Pagination

from typing import TypeVar, Generic
from pydantic import BaseModel

T = TypeVar('T')

class Page(BaseModel, Generic[T]):
    items: list[T]
    total: int
    page: int
    size: int
    pages: int

async def paginate(query, page: int, size: int):
    total = await db.execute(select(func.count()).select_from(query.subquery()))
    total = total.scalar()
    
    items = await db.execute(query.offset((page - 1) * size).limit(size))
    items = items.scalars().all()
    
    return Page(
        items=items,
        total=total,
        page=page,
        size=size,
        pages=(total + size - 1) // size,
    )

@app.get("/users", response_model=Page[UserOut])
async def list_users(page: int = 1, size: int = 10, db: AsyncSession = Depends(get_db)):
    query = select(User).order_by(User.created_at.desc())
    return await paginate(query, page, size)

2. Background Tasks

from fastapi import BackgroundTasks

def send_email(email: str, subject: str, body: str):
    # Send email (runs after response)
    pass

@app.post("/users")
async def create_user(user: UserCreate, background_tasks: BackgroundTasks):
    # Create user in DB
    new_user = await db.create(user)
    
    # Send welcome email in background
    background_tasks.add_task(send_email, user.email, "Welcome!", "Thanks for joining")
    
    return new_user

3. File Upload

from fastapi import UploadFile, File

@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
    content = await file.read()
    # Process file
    return {"filename": file.filename, "size": len(content)}

@app.post("/upload-multiple")
async def upload_multiple(files: list[UploadFile] = File(...)):
    return [{"filename": f.filename} for f in files]

4. WebSocket

from fastapi import WebSocket, WebSocketDisconnect

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    try:
        while True:
            data = await websocket.receive_text()
            await websocket.send_text(f"Echo: {data}")
    except WebSocketDisconnect:
        print("Client disconnected")

5. CORS

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],  # Frontend URL
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Karpathy Principle: Surgical Changes - Add middleware (CORS, auth, logging) one at a time. Test after each addition. Middleware order matters - CORS must come before exception handlers or errors won't have CORS headers.

Karpathy Principle: Own Your Dependencies - Pin versions in requirements.txt. Starlette/Pydantic major upgrades break APIs. Use pip freeze > requirements.txt after testing.

Common Pitfalls

1. Mixing Sync/Async

❌ Bad (blocks event loop):

@app.get("/users")
async def get_users(db: Session = Depends(get_db)):  # Sync Session!
    users = db.query(User).all()  # Blocks!
    return users

✅ Good (fully async):

@app.get("/users")
async def get_users(db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(User))
    users = result.scalars().all()
    return users

2. Mutable Default Arguments

❌ Bad (shared state):

class Item(BaseModel):
    tags: list[str] = []  # Same list for all instances!

✅ Good (factory):

from pydantic import Field

class Item(BaseModel):
    tags: list[str] = Field(default_factory=list)

3. Missing Status Codes

❌ Bad (returns 200 for creation):

@app.post("/users")
async def create_user(user: UserCreate):
    return await db.create(user)

✅ Good (returns 201):

@app.post("/users", status_code=201)
async def create_user(user: UserCreate):
    return await db.create(user)

4. No Error Handling

❌ Bad (500 for not found):

@app.get("/users/{user_id}")
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(User).where(User.id == user_id))
    return result.scalar_one()  # Raises if not found!

✅ Good (404 for not found):

@app.get("/users/{user_id}")
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user

5. Blocking CPU Work in Async

❌ Bad (blocks event loop):

@app.post("/process")
async def process_data(data: dict):
    result = expensive_computation(data)  # Blocks!
    return result

✅ Good (offload to executor):

import asyncio
from concurrent.futures import ProcessPoolExecutor

executor = ProcessPoolExecutor()

@app.post("/process")
async def process_data(data: dict):
    loop = asyncio.get_event_loop()
    result = await loop.run_in_executor(executor, expensive_computation, data)
    return result

Verification Checklist

Before deploying FastAPI applications:

Code Quality

  • All endpoints use async/await correctly
  • Database drivers are async (asyncpg, aiomysql)
  • No blocking operations in async endpoints
  • Pydantic models use Field(default_factory=...) for mutable defaults
  • Type hints on all function parameters

API Design

  • POST endpoints return 201 status code
  • Error responses use HTTPException
  • Pagination on list endpoints
  • Validation on all inputs
  • Consistent response formats

Security

  • Authentication on protected endpoints
  • CORS configured (if needed)
  • No sensitive data in logs
  • Rate limiting (if public API)
  • Input validation on all user data

Performance

  • Database queries are async
  • Connection pooling configured
  • CPU-bound work offloaded to executors
  • File uploads limited in size
  • Background tasks for slow operations

Integration with Other Skills

With PostgreSQL / Prisma

  • SQLAlchemy 2.0 with async support
  • Alembic for migrations
  • Connection pooling

With Redis

  • Async Redis client (redis-py with asyncio)
  • Caching with @lru_cache or Redis
  • Session storage

With Docker

  • Multi-stage build for smaller images
  • Health check endpoint
  • uvicorn with workers

With Next.js / React

  • CORS middleware
  • JWT authentication
  • OpenAPI client generation

References

Official Docs

Tutorials

Tools

Meta: Skill Quality

Completeness: ✅ Comprehensive (9/9 sections)
Karpathy Principles: ✅ 5 mentions
Code Examples: ✅ 20+ working examples
Production Tested: ✅ Used in MemoryRelay API
Last Updated: 2026-04-13

Coverage:

  • ✅ Application setup and lifespan
  • ✅ Dependency injection
  • ✅ Request validation (Pydantic)
  • ✅ Error handling
  • ✅ Pagination
  • ✅ Background tasks
  • ✅ File uploads
  • ✅ WebSocket support
  • ✅ CORS configuration
  • ✅ Async/await best practices

Skill Level: Intermediate
Time to Learn: 4-6 hours
Prerequisites Met: Python, async/await, type hints

Known Gaps: None - production-ready

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.