home / skills / personamanagmentlayer / pcl / fastapi-expert

fastapi-expert skill

/stdlib/api/fastapi-expert

This skill guides building high-performance FastAPI APIs with type safety, async support, and robust authentication and documentation.

npx playbooks add skill personamanagmentlayer/pcl --skill fastapi-expert

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
7.7 KB
---
name: fastapi-expert
version: 1.0.0
description: Expert-level FastAPI development for high-performance Python APIs with async support
category: api
tags: [fastapi, python, api, async, rest, openapi, pydantic]
allowed-tools:
  - Read
  - Write
  - Edit
  - Bash(python:*, pip:*, uvicorn:*)
---

# FastAPI Expert

Expert guidance for FastAPI - modern, fast Python web framework for building APIs with automatic OpenAPI documentation and type safety.

## Core Concepts

### FastAPI Features
- Fast performance (Starlette + Pydantic)
- Automatic OpenAPI/Swagger docs
- Type hints and validation
- Async/await support
- Dependency injection
- OAuth2 and JWT
- WebSocket support

### Key Components
- Path operations (routes)
- Request/response models (Pydantic)
- Dependency injection
- Middleware
- Background tasks
- Testing with TestClient

## Basic FastAPI Application

```python
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel, EmailStr, Field
from typing import List, Optional
import uvicorn

app = FastAPI(
    title="My API",
    description="Production-ready FastAPI",
    version="1.0.0"
)

# Models
class UserCreate(BaseModel):
    email: EmailStr
    name: str = Field(..., min_length=2, max_length=100)
    age: Optional[int] = Field(None, ge=0, le=150)

class UserResponse(BaseModel):
    id: int
    email: str
    name: str

    class Config:
        from_attributes = True

# Routes
@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(user: UserCreate):
    # Create user in database
    db_user = await db.users.create(**user.dict())
    return db_user

@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int):
    user = await db.users.get(user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user

@app.get("/users", response_model=List[UserResponse])
async def list_users(skip: int = 0, limit: int = 100):
    return await db.users.find_many(skip=skip, limit=limit)

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)
```

## Dependency Injection

```python
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.ext.asyncio import AsyncSession

# Database dependency
async def get_db() -> AsyncSession:
    async with async_session() as session:
        yield session

# Auth dependency
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

async def get_current_user(
    token: str = Depends(oauth2_scheme),
    db: AsyncSession = Depends(get_db)
):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials"
    )

    payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
    user_id: int = payload.get("sub")
    if user_id is None:
        raise credentials_exception

    user = await db.get(User, user_id)
    if user is None:
        raise credentials_exception

    return user

# Use dependencies
@app.get("/me", response_model=UserResponse)
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user

@app.post("/posts")
async def create_post(
    post: PostCreate,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    db_post = Post(**post.dict(), user_id=current_user.id)
    db.add(db_post)
    await db.commit()
    return db_post
```

## Authentication

```python
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password: str) -> str:
    return pwd_context.hash(password)

def create_access_token(data: dict, expires_delta: timedelta = None):
    to_encode = data.copy()
    expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

@app.post("/token")
async def login(
    form_data: OAuth2PasswordRequestForm = Depends(),
    db: AsyncSession = Depends(get_db)
):
    user = await authenticate_user(db, form_data.username, form_data.password)
    if not user:
        raise HTTPException(status_code=401, detail="Invalid credentials")

    access_token = create_access_token(data={"sub": str(user.id)})
    return {"access_token": access_token, "token_type": "bearer"}
```

## Database Integration (SQLAlchemy)

```python
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy import Column, Integer, String, Boolean

DATABASE_URL = "postgresql+asyncpg://user:password@localhost/dbname"

engine = create_async_engine(DATABASE_URL, echo=True)
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

Base = declarative_base()

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True, index=True)
    email = Column(String, unique=True, index=True, nullable=False)
    hashed_password = Column(String, nullable=False)
    is_active = Column(Boolean, default=True)

# CRUD operations
async def get_user(db: AsyncSession, user_id: int):
    return await db.get(User, user_id)

async def create_user(db: AsyncSession, user: UserCreate):
    hashed_password = get_password_hash(user.password)
    db_user = User(email=user.email, hashed_password=hashed_password)
    db.add(db_user)
    await db.commit()
    await db.refresh(db_user)
    return db_user
```

## Request Validation

```python
from pydantic import BaseModel, validator, root_validator
from typing import Optional
from datetime import date

class PostCreate(BaseModel):
    title: str = Field(..., min_length=5, max_length=200)
    content: str = Field(..., min_length=10)
    published: bool = False
    tags: List[str] = Field(default_factory=list, max_items=10)

    @validator('tags')
    def validate_tags(cls, v):
        return [tag.lower().strip() for tag in v]

    @validator('title')
    def validate_title(cls, v):
        if any(word in v.lower() for word in ['spam', 'xxx']):
            raise ValueError('Invalid content')
        return v
```

## Background Tasks

```python
from fastapi import BackgroundTasks

def send_email(email: str, message: str):
    # Send email
    print(f"Sending email to {email}: {message}")

@app.post("/send-notification")
async def send_notification(
    email: str,
    background_tasks: BackgroundTasks
):
    background_tasks.add_task(send_email, email, "Welcome!")
    return {"message": "Notification sent"}
```

## Testing

```python
from fastapi.testclient import TestClient
import pytest

client = TestClient(app)

def test_create_user():
    response = client.post(
        "/users",
        json={"email": "[email protected]", "name": "Test User"}
    )
    assert response.status_code == 201
    assert response.json()["email"] == "[email protected]"

def test_get_user():
    response = client.get("/users/1")
    assert response.status_code == 200
    assert "email" in response.json()

@pytest.mark.asyncio
async def test_async_endpoint():
    async with AsyncClient(app=app, base_url="http://test") as ac:
        response = await ac.get("/users")
    assert response.status_code == 200
```

## Best Practices

- Use Pydantic models for validation
- Implement proper error handling
- Use dependency injection
- Add rate limiting
- Enable CORS properly
- Use async/await for I/O
- Document with docstrings
- Add proper logging

## Resources

- FastAPI Docs: https://fastapi.tiangolo.com/
- Pydantic: https://docs.pydantic.dev/
- Starlette: https://www.starlette.io/

Overview

This skill provides expert-level FastAPI development guidance for building high-performance, production-ready Python APIs with full async support. It focuses on practical patterns for routing, validation, auth, database integration, background tasks, and automated docs. The goal is to accelerate safe, maintainable API delivery with modern best practices.

How this skill works

It inspects common FastAPI components and patterns and recommends concrete implementations: Pydantic models for validation, async SQLAlchemy sessions for database access, OAuth2/JWT flows for authentication, dependency injection for reusable logic, and background tasks for non-blocking work. The skill highlights testing strategies, request/response modeling, middleware, and OpenAPI documentation to ensure reliable, observable APIs.

When to use it

  • Building new async REST or GraphQL-style APIs in Python with FastAPI
  • Adding authentication, JWT/OAuth2, and secure dependency-based auth
  • Integrating async databases (Postgres, MySQL) with SQLAlchemy or ORMs
  • Improving request validation, error handling, and OpenAPI docs
  • Writing robust tests for sync and async endpoints

Best practices

  • Define clear Pydantic request/response models and reuse them across routes
  • Use async/await for all I/O (database, HTTP, file) to maximize throughput
  • Encapsulate resources in dependencies (DB sessions, auth) and yield them safely
  • Keep business logic out of route handlers; use services or repositories
  • Add structured logging, error handling, CORS and rate limiting in middleware

Example use cases

  • User management API with registration, login (JWT), profile and list endpoints
  • Content platform with validated post creation, tags normalization and spam checks
  • Background notification system that queues email sends without blocking requests
  • Async CRUD service backed by PostgreSQL using SQLAlchemy async sessions
  • Comprehensive test suite using TestClient and AsyncClient for sync/async coverage

FAQ

Should I use Pydantic models for both requests and responses?

Yes. Use Pydantic models for request validation and response serialization to ensure type safety, automatic docs, and consistent shapes across your API.

How do I handle authentication securely?

Use OAuth2 with JWT for stateless auth, validate tokens in a dependency that checks the DB and token claims, and rotate secrets and expirations appropriately.