BudiBadu Logo
Samplebadu

FastAPI by Example: Pydantic Validation Rules

FastAPI 0.100+

Sometimes built-in types aren't enough. This code example demonstrates how to define custom validation logic using the `@field_validator` decorator to enforce complex business rules.

Code

from fastapi import FastAPI
from pydantic import BaseModel, field_validator

app = FastAPI()

class Order(BaseModel):
    item_name: str
    quantity: int
    price_per_unit: float
    
    @field_validator('item_name')
    @classmethod
    def name_must_be_capitalized(cls, v: str) -> str:
        if not v[0].isupper():
            raise ValueError('Item name must start with a capital letter')
        return v
    
    @field_validator('quantity')
    @classmethod
    def quantity_must_be_positive(cls, v: int) -> int:
        if v <= 0:
            raise ValueError('Quantity must be a positive integer')
        return v

@app.post("/orders/")
async def create_order(order: Order):
    total = order.quantity * order.price_per_unit
    return {"item": order.item_name, "total_cost": total}

Explanation

Pydantic allows developers to define custom validation logic using the @field_validator decorator. This is essential when data requirements go beyond simple type checks or standard constraints like string length. By defining a class method decorated with @field_validator, you can execute arbitrary Python code to verify the value of a specific field. If the value is invalid, raising a ValueError or AssertionError will cause Pydantic to reject the request and FastAPI to return a 422 error. Using custom validators provides several advantages:

  • Automatic Validation: Eliminates manual checks by validating data against defined models.
  • Type Safety: Ensures data integrity and improves developer experience with IDE support.
  • Reduced Boilerplate: Minimizes repetitive validation code, allowing focus on business logic.

For more complex scenarios involving multiple fields, Pydantic offers model-level validators. However, field validators are the primary tool for ensuring individual data points meet specific business rules, such as checking if a username is unique or if a quantity is positive. This keeps the validation logic encapsulated within the model, ensuring that any part of the application using this model adheres to the same rules.

Code Breakdown

12
@field_validator('item_name') marks the method as a validator for the item_name field.
15
if not v[0].isupper(): checks if the first letter of the name is uppercase.
16
raise ValueError(...) triggers a validation error if the condition is not met.
17
return v returns the validated (and potentially modified) value.