CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-flask-openapi3

Generate REST API and OpenAPI documentation for your Flask project with automatic request/response validation using Pydantic models.

Pending
Overview
Eval results
Files

validation.mddocs/

Request Validation

Automatic request validation using Pydantic models for different parameter types. Flask-OpenAPI3 automatically validates incoming requests against Pydantic models and handles validation errors with customizable responses.

Capabilities

Automatic Request Validation

The core validation decorator that automatically validates requests based on function parameter type hints.

def validate_request():
    """
    Decorator for automatic request validation against Pydantic models.
    
    Applied automatically when using typed parameters in route handlers.
    Validates path, query, header, cookie, form, and body parameters.
    
    Raises:
        ValidationError: When request data doesn't match the expected schema
    """

Usage Example:

from flask_openapi3 import OpenAPI, Info
from pydantic import BaseModel

app = OpenAPI(__name__, info=Info(title="API", version="1.0.0"))

class UserQuery(BaseModel):
    name: str
    age: int = None

class UserBody(BaseModel):
    email: str
    password: str

# Validation applied automatically based on parameter types
@app.post("/users")
def create_user(query: UserQuery, body: UserBody):
    # query and body are automatically validated
    return {"message": f"User {query.name} created with email {body.email}"}

Internal Validation Functions

Core validation logic for different parameter types.

def _validate_request(
    header: Optional[Type[BaseModel]] = None,
    cookie: Optional[Type[BaseModel]] = None,
    path: Optional[Type[BaseModel]] = None,
    query: Optional[Type[BaseModel]] = None,
    form: Optional[Type[BaseModel]] = None,
    body: Optional[Type[BaseModel]] = None,
    raw: Optional[Type[RawModel]] = None,
    path_kwargs: Optional[dict[Any, Any]] = None,
) -> dict:
    """
    Core request validation logic.
    
    Args:
        header: Pydantic model for header parameters
        cookie: Pydantic model for cookie parameters
        path: Pydantic model for path parameters
        query: Pydantic model for query parameters
        form: Pydantic model for form data
        body: Pydantic model for request body
        raw: RawModel for raw request data
        path_kwargs: Path parameters extracted from URL
        
    Returns:
        Dictionary of validated request parameters
    """

Path Parameter Validation

Validates URL path parameters against Pydantic models.

def _validate_path(path: Type[BaseModel], path_kwargs: dict, func_kwargs: dict):
    """
    Validate path parameters from URL route.
    
    Args:
        path: Pydantic model defining expected path parameters
        path_kwargs: Extracted path parameters from URL
        func_kwargs: Function arguments to populate
        
    Raises:
        ValidationError: When path parameters don't match model
    """

Usage Example:

from pydantic import BaseModel

class UserPath(BaseModel):
    user_id: int

@app.get("/users/<int:user_id>")
def get_user(path: UserPath):
    # path.user_id is validated as integer
    return {"user_id": path.user_id}

Query Parameter Validation

Validates URL query parameters against Pydantic models.

def _validate_query(query: Type[BaseModel], func_kwargs: dict):
    """
    Validate query parameters from URL query string.
    
    Args:
        query: Pydantic model defining expected query parameters
        func_kwargs: Function arguments to populate
        
    Raises:
        ValidationError: When query parameters don't match model
    """

Usage Example:

from pydantic import BaseModel
from typing import Optional

class SearchQuery(BaseModel):
    q: str
    limit: int = 10
    offset: int = 0
    category: Optional[str] = None

@app.get("/search")
def search(query: SearchQuery):
    # Query parameters automatically validated and converted
    return {
        "query": query.q,
        "limit": query.limit,
        "offset": query.offset,
        "category": query.category
    }

Header Parameter Validation

Validates HTTP headers against Pydantic models.

def _validate_header(header: Type[BaseModel], func_kwargs: dict):
    """
    Validate HTTP headers.
    
    Args:
        header: Pydantic model defining expected headers
        func_kwargs: Function arguments to populate
        
    Raises:
        ValidationError: When headers don't match model
    """

Usage Example:

from pydantic import BaseModel, Field
from typing import Optional

class AuthHeaders(BaseModel):
    authorization: str = Field(alias="Authorization")
    content_type: str = Field(alias="Content-Type", default="application/json")
    user_agent: Optional[str] = Field(alias="User-Agent", default=None)

@app.post("/protected")
def protected_endpoint(header: AuthHeaders, body: dict):
    # Headers automatically validated and available
    auth_token = header.authorization
    return {"authorized": True}

Cookie Parameter Validation

Validates HTTP cookies against Pydantic models.

def _validate_cookie(cookie: Type[BaseModel], func_kwargs: dict):
    """
    Validate HTTP cookies.
    
    Args:
        cookie: Pydantic model defining expected cookies
        func_kwargs: Function arguments to populate
        
    Raises:
        ValidationError: When cookies don't match model
    """

Usage Example:

from pydantic import BaseModel
from typing import Optional

class SessionCookies(BaseModel):
    session_id: str
    preferences: Optional[str] = None

@app.get("/profile")
def get_profile(cookie: SessionCookies):
    # Cookies automatically validated
    return {"session": cookie.session_id}

Form Data Validation

Validates form data (application/x-www-form-urlencoded or multipart/form-data) against Pydantic models.

def _validate_form(form: Type[BaseModel], func_kwargs: dict):
    """
    Validate form data from request.
    
    Args:
        form: Pydantic model defining expected form fields
        func_kwargs: Function arguments to populate
        
    Raises:
        ValidationError: When form data doesn't match model
    """

Usage Example:

from pydantic import BaseModel
from flask_openapi3 import FileStorage

class UserForm(BaseModel):
    name: str
    email: str
    age: int
    avatar: Optional[FileStorage] = None

@app.post("/users/form")
def create_user_form(form: UserForm):
    # Form data including file uploads validated
    return {
        "name": form.name,
        "email": form.email,
        "age": form.age,
        "has_avatar": form.avatar is not None
    }

Request Body Validation

Validates JSON request bodies against Pydantic models.

def _validate_body(body: Type[BaseModel], func_kwargs: dict):
    """
    Validate JSON request body.
    
    Args:
        body: Pydantic model defining expected body structure
        func_kwargs: Function arguments to populate
        
    Raises:
        ValidationError: When body doesn't match model
    """

Usage Example:

from pydantic import BaseModel, EmailStr, validator
from typing import Optional

class CreateUserBody(BaseModel):
    name: str
    email: EmailStr
    password: str
    age: Optional[int] = None
    
    @validator('password')
    def validate_password(cls, v):
        if len(v) < 8:
            raise ValueError('Password must be at least 8 characters')
        return v

@app.post("/users")
def create_user(body: CreateUserBody):
    # JSON body automatically validated with custom validators
    return {"id": 1, "name": body.name, "email": body.email}

Raw Data Validation

Validates raw request data for custom content types.

class RawModel(Request):
    """Raw request data handling"""
    mimetypes: list[str] = ["application/json"]

Usage Example:

from flask_openapi3 import RawModel

class CSVRawModel(RawModel):
    mimetypes = ["text/csv", "application/csv"]

@app.post("/upload-csv")
def upload_csv(raw: CSVRawModel):
    # Raw CSV data accessible via raw.data
    csv_content = raw.data.decode('utf-8')
    return {"rows": len(csv_content.split('\n'))}

Validation Error Handling

Default Error Models

Built-in models for validation error responses.

class ValidationErrorModel(BaseModel):
    """Default validation error response format"""
    detail: list[dict[str, Any]]

class UnprocessableEntity(BaseModel):
    """422 error response format"""  
    detail: list[dict[str, Any]]

Custom Validation Error Handling

You can customize validation error responses by providing custom error models and callbacks:

from flask_openapi3 import OpenAPI, Info
from pydantic import BaseModel

class CustomErrorModel(BaseModel):
    error: str
    field_errors: dict[str, list[str]]
    status_code: int

def custom_error_callback(e):
    errors = {}
    for error in e.errors():
        field = ".".join(str(x) for x in error["loc"])
        if field not in errors:
            errors[field] = []
        errors[field].append(error["msg"])
    
    return {
        "error": "Validation failed",
        "field_errors": errors,
        "status_code": 422
    }

app = OpenAPI(
    __name__,
    info=Info(title="API", version="1.0.0"),
    validation_error_model=CustomErrorModel,
    validation_error_callback=custom_error_callback,
    validation_error_status=422
)

Advanced Validation Features

Field Aliases

Use Pydantic field aliases to map between different parameter names:

from pydantic import BaseModel, Field

class UserQuery(BaseModel):
    user_id: int = Field(alias="userId")
    full_name: str = Field(alias="fullName")

@app.get("/users")
def get_users(query: UserQuery):
    # URL: /users?userId=123&fullName=John
    return {"user_id": query.user_id, "name": query.full_name}

Custom Validators

Use Pydantic validators for complex validation logic:

from pydantic import BaseModel, validator
import re

class UserBody(BaseModel):
    username: str
    email: str
    
    @validator('username')
    def validate_username(cls, v):
        if not re.match(r'^[a-zA-Z0-9_]+$', v):
            raise ValueError('Username can only contain letters, numbers, and underscores')
        return v
    
    @validator('email')
    def validate_email(cls, v):
        if '@' not in v:
            raise ValueError('Invalid email format')
        return v.lower()

Nested Models

Support for complex nested validation:

from pydantic import BaseModel
from typing import List, Optional

class Address(BaseModel):
    street: str
    city: str
    country: str
    postal_code: str

class User(BaseModel):
    name: str
    email: str
    addresses: List[Address]
    primary_address: Optional[Address] = None

@app.post("/users")
def create_user(body: User):
    # Nested validation automatically applied
    return {"user_created": True, "address_count": len(body.addresses)}

Install with Tessl CLI

npx tessl i tessl/pypi-flask-openapi3

docs

cli-commands.md

core-classes.md

index.md

openapi-models.md

utilities.md

validation.md

tile.json