Beginners Guide: How to Use Pydantic for Data Validation in FastAPI

In web development, data validation is a critical step to ensure the security and stable operation of applications. For example, during user registration, it is necessary to verify whether the username meets the rules, whether the age is a valid number, and whether the email format is correct. If the validation logic is improperly handled, it may result in error messages at best or even program crashes or security vulnerabilities at worst. As a high-performance Python web framework, FastAPI integrates the Pydantic library for data validation, making data validation simple and efficient.

I. What is Pydantic?

Pydantic is a Python library whose core functionality is data validation and settings management. It automatically checks whether input data conforms to model rules by defining data models (based on Python type hints) and provides clear error messages when data is invalid. FastAPI chose Pydantic as its data validation tool because it:
- Is compatible with Python native type hints, requiring no additional learning of complex syntax;
- Automatically handles type conversion (e.g., string to integer);
- Includes built-in common validation logic (required fields, default values, format validation, etc.);
- Provides user-friendly error messages for easier front-end debugging.

II. Quick Start: Define Your First Pydantic Model

Pydantic models are defined by inheriting from BaseModel, with field types specified by Python type hints. Here is a simple example of a user information model:

from pydantic import BaseModel

class User(BaseModel):
    name: str  # String type, required (no default value)
    age: int = 18  # Integer type, optional (default value 18)
    email: str | None = None  # String or None, optional (default None)

Key Points:
- Required Fields: If a field has no default value (e.g., name), it must be provided by the user; otherwise, FastAPI will return a “field required” error.
- Optional Fields: If a field has a default value (e.g., age and email), users can omit it and use the default value.
- Type Hints: Supports basic types like int, str, bool, and complex types like list and dict (e.g., hobbies: list[str]).

III. Using Pydantic to Handle Request Data in FastAPI

In FastAPI, Pydantic models are most commonly used to process request data (e.g., the request body in a POST request). FastAPI automatically parses the request body data into a Pydantic model and validates its legality. If validation fails, FastAPI returns a 422 error with detailed error messages.

Step 1: Define the Pydantic Model

from pydantic import BaseModel
from fastapi import FastAPI

# Define user registration request model
class UserCreate(BaseModel):
    name: str  # Required, string
    age: int  # Required, integer (no default value)
    email: str  # Required, string (custom validation to be added later)

Step 2: Use the Model in a Route Function

FastAPI route functions can directly specify parameters of Pydantic model types, and FastAPI will automatically parse the request body and validate the data:

app = FastAPI()

@app.post("/users/register")
def register_user(user: UserCreate):
    # After successful validation, 'user' is an instance of UserCreate and can be used directly
    return {
        "message": f"User registration successful! Welcome, {user.name} ({user.age} years old)",
        "email": user.email
    }

Test the Validation Effect

After starting the FastAPI service, you can test the validation with a POST request:
- Normal Request: {"name": "小明", "age": 20, "email": "xiaoming@example.com"} → Returns a success message;
- Error Request: {"name": "小红", "age": "abc"} (invalid age type) → FastAPI automatically returns a 422 error with the following content:

{
  "detail": [
    {
      "loc": ["body", "age"],
      "msg": "Input should be a valid integer",
      "type": "type_error.integer"
    }
  ]
}

IV. Response Data Validation: Ensure Correct Return Format

Pydantic can also be used to validate response data (e.g., JSON returned to the front-end) to ensure the returned format meets expectations. For example, define a model to return user information:

class UserResponse(BaseModel):
    id: int  # User ID (assumed generated by the database)
    name: str
    age: int
    email: str | None = None  # Optional field

@app.get("/users/{user_id}", response_model=UserResponse)
def get_user(user_id: int):
    # Simulate fetching user data from the database
    user_data = {
        "id": user_id,
        "name": "Test User",
        "age": 25,
        "email": "test@example.com"
    }
    return user_data  # FastAPI automatically validates if the return data conforms to the UserResponse model

Key Points:
- Use the response_model=UserResponse parameter, and FastAPI will automatically serialize the returned data into the format defined by UserResponse.
- If the returned data contains fields outside the model (e.g., an extra token), FastAPI will automatically filter them to ensure only the fields defined in the model are returned.

V. Advanced: Custom Validation Rules

Pydantic supports more complex validation requirements, such as limiting numerical ranges and custom formats (e.g., email). This can be achieved through the following methods:

1. Use Field to Set Field Constraints

The Field function can specify additional rules for fields (e.g., minimum/maximum length, regular expressions):

from pydantic import BaseModel, Field, EmailStr

class UserCreate(BaseModel):
    name: str = Field(..., min_length=2, max_length=20)  # Name length 2-20
    age: int = Field(gt=0, lt=150)  # Age: 0 < age < 150
    email: EmailStr = Field(...)  # Automatically validates email format (requires 'pydantic[email]' installation)

2. Custom Validation Functions

Use the @field_validator decorator to define custom validation logic (e.g., email format):

from pydantic import BaseModel, field_validator
import re

class UserCreate(BaseModel):
    name: str
    age: int
    email: str

    @field_validator('email')  # Validate the 'email' field
    def email_format(cls, v):
        if not re.match(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', v):
            raise ValueError('Invalid email format')
        return v  # Return the original value if validation passes

VI. Summary

Pydantic acts as a “data validation butler” in FastAPI, simplifying API development with the following advantages:
- Automatic Validation: FastAPI + Pydantic automatically intercepts invalid data without manual if-else checks;
- Error Messages: Clear error messages help front-end developers quickly identify issues;
- Type Safety: Avoid program crashes caused by data type errors;
- Flexible Extension: Supports basic types, complex structures, custom rules, etc., meeting various validation needs.

For FastAPI beginners, mastering basic Pydantic usage (defining models, route parameters, error handling) is essential for developing secure and robust APIs.

Tip: For further learning, refer to the official Pydantic documentation (https://docs.pydantic.dev/latest/) to explore advanced features like nested models, list nesting, and ORM integration (orm_mode=True).

Xiaoye