Claude
Skills
Sign in
Back

fastapi-validation

Included with Lifetime
$97 forever

Use when FastAPI validation with Pydantic models. Use when building type-safe APIs with robust request/response validation.

Backend & APIs

What this skill does


# FastAPI Validation

Master FastAPI validation with Pydantic for building type-safe APIs
with comprehensive request and response validation.

## Pydantic BaseModel Fundamentals

Core Pydantic patterns with Pydantic v2.

```python
from pydantic import BaseModel, Field, ConfigDict
from typing import Optional
from datetime import datetime

# Basic model
class User(BaseModel):
    id: int
    name: str
    email: str
    created_at: datetime

# With defaults and optional fields
class UserCreate(BaseModel):
    name: str
    email: str
    age: Optional[int] = None
    is_active: bool = True

# With Field constraints
class Product(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    price: float = Field(..., gt=0, le=1000000)
    quantity: int = Field(default=0, ge=0)
    description: Optional[str] = Field(None, max_length=500)

    model_config = ConfigDict(
        str_strip_whitespace=True,
        validate_assignment=True,
        json_schema_extra={
            'example': {
                'name': 'Widget',
                'price': 29.99,
                'quantity': 100,
                'description': 'A useful widget'
            }
        }
    )
```

## Request Body Validation

Validating complex request bodies.

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

app = FastAPI()

# Simple request validation
class CreateUserRequest(BaseModel):
    username: str = Field(..., min_length=3, max_length=50)
    email: EmailStr
    password: str = Field(..., min_length=8)
    age: int = Field(..., ge=13, le=120)

@app.post('/users')
async def create_user(user: CreateUserRequest):
    # user is automatically validated
    return {'username': user.username, 'email': user.email}

# Nested models
class Address(BaseModel):
    street: str
    city: str
    state: str = Field(..., min_length=2, max_length=2)
    zip_code: str = Field(..., pattern=r'^\d{5}(-\d{4})?$')

class UserProfile(BaseModel):
    name: str
    email: EmailStr
    address: Address
    phone: Optional[str] = Field(None, pattern=r'^\+?1?\d{9,15}$')

@app.post('/profiles')
async def create_profile(profile: UserProfile):
    return profile

# List validation
class BulkCreateRequest(BaseModel):
    users: List[CreateUserRequest] = Field(..., min_length=1, max_length=100)

@app.post('/users/bulk')
async def bulk_create_users(request: BulkCreateRequest):
    return {'count': len(request.users)}

# Complex nested structures
class Tag(BaseModel):
    name: str
    color: str = Field(..., pattern=r'^#[0-9A-Fa-f]{6}$')

class Post(BaseModel):
    title: str = Field(..., min_length=1, max_length=200)
    content: str
    tags: List[Tag] = []
    author: UserProfile
    published: bool = False

@app.post('/posts')
async def create_post(post: Post):
    return post
```

## Query Parameter Validation

Validating query parameters with Field constraints.

```python
from fastapi import FastAPI, Query
from typing import Optional, List
from enum import Enum

app = FastAPI()

# Simple query params
@app.get('/users')
async def get_users(
    skip: int = Query(0, ge=0),
    limit: int = Query(10, ge=1, le=100),
    search: Optional[str] = Query(None, min_length=3, max_length=50)
):
    return {'skip': skip, 'limit': limit, 'search': search}

# Enum validation
class SortOrder(str, Enum):
    asc = 'asc'
    desc = 'desc'

class SortField(str, Enum):
    name = 'name'
    created_at = 'created_at'
    updated_at = 'updated_at'

@app.get('/items')
async def get_items(
    sort_by: SortField = Query(SortField.created_at),
    order: SortOrder = Query(SortOrder.desc)
):
    return {'sort_by': sort_by, 'order': order}

# Multiple values
@app.get('/filter')
async def filter_items(
    tags: List[str] = Query([]),
    categories: List[int] = Query([], max_length=10)
):
    return {'tags': tags, 'categories': categories}

# Regex pattern
@app.get('/search')
async def search(
    q: str = Query(..., min_length=1, max_length=100, pattern=r'^[a-zA-Z0-9\s]+$')
):
    return {'query': q}
```

## Path Parameter Validation

Validating URL path parameters.

```python
from fastapi import FastAPI, Path
from typing import Annotated

app = FastAPI()

@app.get('/users/{user_id}')
async def get_user(
    user_id: int = Path(..., gt=0, description='The user ID')
):
    return {'user_id': user_id}

@app.get('/items/{item_id}/reviews/{review_id}')
async def get_review(
    item_id: Annotated[int, Path(gt=0)],
    review_id: Annotated[int, Path(gt=0)]
):
    return {'item_id': item_id, 'review_id': review_id}

# String path validation
@app.get('/categories/{category_name}')
async def get_category(
    category_name: str = Path(..., min_length=1, max_length=50, pattern=r'^[a-z-]+$')
):
    return {'category': category_name}
```

## Custom Validators

Field validators and model validators with Pydantic v2.

```python
from pydantic import BaseModel, field_validator, model_validator
from typing import Any
import re

class UserRegistration(BaseModel):
    username: str
    email: str
    password: str
    password_confirm: str

    @field_validator('username')
    @classmethod
    def username_alphanumeric(cls, v: str) -> str:
        if not re.match(r'^[a-zA-Z0-9_]+$', v):
            raise ValueError('Username must be alphanumeric')
        if len(v) < 3:
            raise ValueError('Username must be at least 3 characters')
        return v.lower()

    @field_validator('email')
    @classmethod
    def validate_email_domain(cls, v: str) -> str:
        if not v.endswith(('@example.com', '@example.org')):
            raise ValueError('Email must be from example.com or example.org')
        return v.lower()

    @field_validator('password')
    @classmethod
    def password_strength(cls, v: str) -> str:
        if len(v) < 8:
            raise ValueError('Password must be at least 8 characters')
        if not re.search(r'[A-Z]', v):
            raise ValueError('Password must contain uppercase letter')
        if not re.search(r'[a-z]', v):
            raise ValueError('Password must contain lowercase letter')
        if not re.search(r'[0-9]', v):
            raise ValueError('Password must contain digit')
        return v

    @model_validator(mode='after')
    def check_passwords_match(self) -> 'UserRegistration':
        if self.password != self.password_confirm:
            raise ValueError('Passwords do not match')
        return self

# Validator with dependencies
class DateRange(BaseModel):
    start_date: datetime
    end_date: datetime

    @model_validator(mode='after')
    def check_dates(self) -> 'DateRange':
        if self.start_date >= self.end_date:
            raise ValueError('start_date must be before end_date')
        return self

# Computed fields
from pydantic import computed_field

class Product(BaseModel):
    name: str
    price: float
    tax_rate: float = 0.1

    @computed_field
    @property
    def price_with_tax(self) -> float:
        return round(self.price * (1 + self.tax_rate), 2)

# Before validator
class UserInput(BaseModel):
    name: str
    email: str

    @field_validator('name', 'email', mode='before')
    @classmethod
    def strip_whitespace(cls, v: Any) -> Any:
        if isinstance(v, str):
            return v.strip()
        return v
```

## Field Types

Specialized field types for validation.

```python
from pydantic import (
    BaseModel,
    EmailStr,
    HttpUrl,
    SecretStr,
    conint,
    constr,
    confloat,
    conlist,
    UUID4,
    IPvAnyAddress,
    FilePath,
    DirectoryPath,
    Json
)
from typing import List
from datetime import date, time

class AdvancedUser(BaseModel):
    # String constraints
    username: constr(min_length=3, max_length=50, pattern=r'^[a-zA-Z0-9_]+$')
    bio: constr(max_length=500) | None = None

    # Email and URL
    email: EmailStr
    website: HttpUrl | None = None

    # Numeric constraints
    age: conint(ge=13, le=120)
    rating: confloat(ge=0

Related in Backend & APIs