0
0
FastAPIframework~15 mins

Custom validation with validator decorator in FastAPI - Deep Dive

Choose your learning style9 modes available
Overview - Custom validation with validator decorator
What is it?
Custom validation with the validator decorator in FastAPI allows you to add your own rules to check data before it is accepted. It is used to make sure the data fits your specific needs beyond basic types. This helps catch mistakes early and keeps your app safe and reliable. The validator decorator is part of Pydantic, which FastAPI uses to handle data validation.
Why it matters
Without custom validation, your app might accept wrong or harmful data, causing bugs or security issues. Custom validation lets you control exactly what data is allowed, making your app more trustworthy and user-friendly. It saves time by catching errors early and gives clear feedback to users about what is wrong.
Where it fits
Before learning custom validation, you should understand basic FastAPI routes and Pydantic models. After mastering this, you can explore more advanced data handling like complex nested models, dependency injection, and error handling in FastAPI.
Mental Model
Core Idea
Custom validation with the validator decorator lets you add special checks to your data models so only correct and meaningful data passes through.
Think of it like...
It's like having a security guard at a party who checks guests' invitations carefully to make sure only the right people get in.
Pydantic Model
┌─────────────────────────┐
│ Fields with basic types │
│ (e.g., str, int, float)  │
└──────────┬──────────────┘
           │
           ▼
┌─────────────────────────┐
│ @validator decorator    │
│ Custom check functions  │
└──────────┬──────────────┘
           │
           ▼
┌─────────────────────────┐
│ Validated data ready    │
│ for FastAPI use         │
└─────────────────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Pydantic Models
🤔
Concept: Learn what Pydantic models are and how they define data shapes in FastAPI.
Pydantic models are Python classes that describe the shape and type of data your app expects. For example, a User model might have a name as a string and age as an integer. FastAPI uses these models to check incoming data automatically.
Result
You can create simple data models that FastAPI uses to validate input data types.
Understanding Pydantic models is key because custom validation builds on top of these models to add more specific rules.
2
FoundationBasic Field Validation
🤔
Concept: Learn how Pydantic automatically validates basic data types and required fields.
When you define a Pydantic model, FastAPI checks if the data matches the types you set. For example, if age is an int, sending a string will cause an error. Fields can also be optional or have default values.
Result
FastAPI rejects data that doesn't match the expected types or misses required fields.
Knowing what Pydantic validates automatically helps you see why custom validation is needed for rules beyond simple types.
3
IntermediateIntroducing the validator Decorator
🤔Before reading on: do you think the validator decorator runs before or after basic type checks? Commit to your answer.
Concept: The validator decorator lets you write custom functions that check or modify field values after basic validation.
You add a method to your Pydantic model and decorate it with @validator, specifying which field to check. This method receives the field value and can raise errors or change the value. For example, you can check if a username is not empty or if an age is positive.
Result
Your model now enforces custom rules, rejecting or adjusting data that doesn't meet them.
Understanding that validators run after basic checks lets you safely assume the data type is correct when writing your custom logic.
4
IntermediateWriting Multiple Validators
🤔Before reading on: can you use multiple validators on the same field? Commit to yes or no.
Concept: You can write several validator methods for different fields or even multiple validators for the same field to handle complex rules.
Each validator method targets one or more fields by name. They run in the order they appear in the code. This allows you to split checks into smaller, focused functions. For example, one validator checks format, another checks value range.
Result
Your model can handle complex validation logic cleanly and maintainably.
Knowing you can chain validators helps organize validation logic and avoid large, hard-to-read functions.
5
IntermediateUsing pre and each_item Options
🤔Before reading on: do you think validators run before or after parsing? Commit to your answer.
Concept: Validator decorators have options like pre=True to run before parsing and each_item=True to validate items in a list individually.
By default, validators run after Pydantic parses the data into the correct type. Setting pre=True runs the validator before parsing, useful for raw input checks. Using each_item=True applies the validator to each element in a list or set.
Result
You gain fine control over when and how validation happens, enabling checks on raw or complex data structures.
Understanding these options lets you handle edge cases and complex data shapes that default validation can't cover.
6
AdvancedRaising Custom Errors in Validators
🤔Before reading on: do you think raising ValueError or a custom exception is better for validation errors? Commit to your answer.
Concept: Validators raise errors to signal invalid data, and you can customize error messages for clarity.
Inside a validator method, if data is invalid, raise ValueError with a clear message. FastAPI catches this and returns a helpful error response to the client. You can also raise TypeError or AssertionError, but ValueError is most common.
Result
Users get precise feedback about what went wrong, improving user experience and debugging.
Knowing how to raise and customize errors in validators is crucial for building user-friendly APIs.
7
ExpertValidator Execution Order and Side Effects
🤔Before reading on: do you think validator methods run in the order they are defined or alphabetically? Commit to your answer.
Concept: Validator methods run in the order they appear in the code, and their results can affect each other, so side effects matter.
If multiple validators modify the same field, the output of one becomes the input of the next. This means order affects the final value. Also, validators should avoid side effects like changing unrelated state because they may run multiple times during model creation.
Result
You can write predictable, safe validators that compose well without unexpected bugs.
Understanding validator order and side effects prevents subtle bugs and helps design clean validation logic.
Under the Hood
When FastAPI receives data, it uses Pydantic to parse and validate it. Pydantic first checks basic types and required fields. Then it calls any validator methods decorated with @validator. These methods receive the field value and can modify it or raise errors. The final validated data is then passed to your FastAPI endpoint. This process happens synchronously and uses Python's class and method mechanisms internally.
Why designed this way?
Pydantic's validator decorator was designed to keep validation logic close to the data model, making code easier to read and maintain. It separates basic type validation from custom rules, allowing flexibility. Alternatives like external validation functions were less integrated and harder to manage. This design balances simplicity, power, and clarity.
Incoming Data
    │
    ▼
Basic Type Checks (Pydantic)
    │
    ▼
@validator Methods Run
    │
    ├─ Modify or Check Field Value
    ├─ Raise Error if Invalid
    │
    ▼
Validated Model Instance
    │
    ▼
Passed to FastAPI Endpoint
Myth Busters - 4 Common Misconceptions
Quick: Does the validator decorator run before or after basic type checks? Commit to your answer.
Common Belief:Validators run before any type checking, so they receive raw input data.
Tap to reveal reality
Reality:Validators run after Pydantic has parsed and checked basic types, so they receive data already converted to the correct type.
Why it matters:If you assume validators get raw data, you might write checks that fail or behave incorrectly, causing bugs or missed errors.
Quick: Can you use the validator decorator to validate multiple fields at once? Commit yes or no.
Common Belief:Each validator can only check one field at a time.
Tap to reveal reality
Reality:Validators can target multiple fields by listing them in the decorator, allowing cross-field validation.
Why it matters:Believing validators are single-field only limits your ability to enforce rules involving relationships between fields.
Quick: Is it safe to have validators modify unrelated parts of the model or external state? Commit yes or no.
Common Belief:Validators can safely change anything, including external variables or other fields.
Tap to reveal reality
Reality:Validators should only modify their target field and avoid side effects because they may run multiple times or unpredictably.
Why it matters:Side effects in validators can cause hard-to-debug bugs and inconsistent data states.
Quick: Does the order of validator methods affect the final validated data? Commit yes or no.
Common Belief:Validator methods run in any order, so order does not matter.
Tap to reveal reality
Reality:Validator methods run in the order they are defined in the code, affecting the final data if multiple modify the same field.
Why it matters:Ignoring order can lead to unexpected validation results and bugs that are hard to trace.
Expert Zone
1
Validators with pre=True run before parsing, allowing raw input checks but require careful handling of types.
2
Using each_item=True on validators enables element-wise validation in collections, which is essential for nested or list data.
3
Validators can be class methods or static methods, but using class methods allows access to the whole model via values parameter for cross-field validation.
When NOT to use
Avoid using validator decorators for very complex validation logic involving multiple models or asynchronous checks. Instead, use FastAPI dependencies or custom request handlers for those cases.
Production Patterns
In production, validators are used to enforce business rules like password strength, date ranges, or unique constraints. They are combined with global error handlers to return consistent API error responses.
Connections
Data Validation in Database ORMs
Both enforce data correctness but at different layers; Pydantic validators check input before database interaction.
Understanding Pydantic validation helps grasp how ORMs also validate data but usually after input reaches the database layer.
Functional Programming Pure Functions
Validators ideally behave like pure functions without side effects, similar to functional programming principles.
Knowing pure function concepts helps write validators that are predictable and safe, avoiding bugs from side effects.
Quality Control in Manufacturing
Validator decorators act like quality inspectors checking each product (data) before it moves forward.
Seeing validation as quality control highlights its role in preventing defects early, saving time and resources.
Common Pitfalls
#1Raising generic exceptions instead of ValueError in validators.
Wrong approach:def check_age(cls, v): if v < 0: raise Exception('Age must be positive') return v
Correct approach:def check_age(cls, v): if v < 0: raise ValueError('Age must be positive') return v
Root cause:Using generic exceptions prevents FastAPI from properly formatting validation errors for clients.
#2Modifying unrelated fields or external state inside a validator.
Wrong approach:def check_name(cls, v): global some_flag some_flag = True return v
Correct approach:def check_name(cls, v): # Only validate or modify 'v', no side effects return v
Root cause:Misunderstanding that validators should be pure and only affect their own field.
#3Assuming validators run before type parsing and writing type-dependent code incorrectly.
Wrong approach:def check_date(cls, v): # v is expected as string, but it's already a date object if len(v) != 10: raise ValueError('Invalid date format') return v
Correct approach:def check_date(cls, v): # v is a date object, so check accordingly if v > date.today(): raise ValueError('Date cannot be in the future') return v
Root cause:Confusing validator timing leads to wrong assumptions about input data type.
Key Takeaways
Custom validation with the validator decorator extends basic type checks to enforce precise data rules.
Validators run after Pydantic parses data, so they receive correctly typed values to check or modify.
You can write multiple validators per model, control their execution order, and customize error messages.
Avoid side effects in validators to keep validation predictable and safe.
Understanding validator options like pre and each_item unlocks powerful validation for complex data.