ESC

Type to search...

Validation

Validate requests with Pydantic, schema engines, and custom processors.

Pydantic Validation

The simplest approach — Pydantic validates automatically:

from pydantic import BaseModel, Field, field_validator

class CreateOrder(BaseModel):
    sandwich: str = Field(min_length=1)
    quantity: int = Field(ge=1, le=100)
    customer_name: str = Field(min_length=2, max_length=100)

    @field_validator("sandwich")
    @classmethod
    def validate_sandwich(cls, v):
        allowed = ["BLT", "Club", "Veggie", "Reuben", "Philly"]
        if v not in allowed:
            raise ValueError(f"Must be one of: {', '.join(allowed)}")
        return v

@post("/orders")
async def create_order(order: CreateOrder) -> dict:
    return {"status": "created", "order": order.model_dump()}
curl -X POST http://localhost:8000/orders \
  -H "Content-Type: application/json" \
  -d '{"sandwich": "INVALID", "quantity": 0}'
# {"detail": "Must be one of: BLT, Club, Veggie, Reuben, Philly"}

Custom Validation Processors

For validation that depends on external data, use processors:

from evoid import register_processor
from evoid.core import Context

async def validate_inventory(ctx: Context) -> dict:
    """Check if sandwich is in stock."""
    body = ctx.intent.metadata.get("body", {})
    sandwich = body.get("sandwich")
    qty = body.get("quantity", 1)

    # Check inventory (simplified)
    in_stock = sandwich in ["BLT", "Club", "Veggie"]
    if not in_stock:
        raise ValueError(f"'{sandwich}' is not available")

    ctx.state["validated"] = True
    return {"validated": True}

register_processor("validate_inventory", validate_inventory)

Wire it to the order endpoint:

from evoid.core.extend import before

before("POST:/orders", "validate_inventory")

Validation as a Pipeline Step

Add validation to the Intent’s pipeline:

from evoid import Intent, Level
from evoid.core.extend import add_intent_with_pipeline

CREATE_ORDER = Intent(name="create_order", level=Level.STANDARD)

async def handle_create_order(intent: Intent) -> dict:
    body = intent.metadata.get("body", {})
    return {"status": "created", "order": body}

add_intent_with_pipeline(
    CREATE_ORDER,
    processors=["validate_inventory", "create_order"],
    handler=handle_create_order,
)

Schema Engine

EVOID can auto-generate JSON Schema from your Intents:

from evoid import export_json_schemas

schemas = export_json_schemas()
# {"create_order": {"type": "object", "properties": {...}, ...}}

Use this for API documentation, OpenAPI specs, or AI agent discovery.

Response Validation

Validate responses too — catch bugs before they reach clients:

from pydantic import BaseModel

class OrderResponse(BaseModel):
    id: int
    status: str
    total: float

async def validate_response(ctx: Context) -> dict:
    """Validate the handler's output."""
    result = ctx.state.get("handler_result", {})
    OrderResponse(**result)  # Raises if invalid
    return {"response_valid": True}

register_processor("validate_response", validate_response)

after("POST:/orders", "validate_response")

What You Learned

ConceptWhat It Is
Pydantic validationAutomatic from type hints and Field constraints
Custom validators@field_validator for complex rules
Validation processorsPipeline steps that check external data
Schema exportJSON Schema from Intents for docs/AI
Response validationValidate output, not just input

Next: Error Handling

Let’s handle errors properly — Error Handling.