Understanding Annotated
Throughout this course, you’ve seen two different ways of writing validations and dependencies.
For example, in Pydantic models, we previously wrote:
Annotated is the recommended approach in modern FastAPI and Pydantic.
Why Use Annotated?
The purpose of Annotated is to separate the actual data type from its metadata.
General syntax:
- Type describes the expected data type.
- Metadata provides additional instructions such as validation rules or dependency information.
Common Examples
Notice that only the metadata changes.
How to Read Annotated
Consider this example.
- The value is a string.
- It must satisfy the validation rules defined by
Field().
- The value is an EmployeeRepository.
- FastAPI should obtain it by calling
get_employee_repository().
Field() vs Depends()
Although both are used with Annotated, they serve different purposes.
Field()
Used inside Pydantic models to validate data.
- Type:
str - Metadata:
Field(min_length=2)
Depends()
Used by FastAPI for Dependency Injection.
- Type:
EmployeeRepository - Metadata:
Depends(get_employee_repository)
Field(), Depends() does not perform validation. Instead, it tells FastAPI how to create the required object.
Creating Reusable Dependency Aliases
Suppose many endpoints need anEmployeeRepository.
Instead of repeatedly writing:
Mental Model
Think ofAnnotated as attaching extra information to a type.
Key Takeaway
Annotated always combines two things:
- The actual type (
str,int,EmployeeRepository, etc.). - Metadata that describes how the value should be validated or obtained.
Field()→ Request body validation.Query()→ Query parameter validation.Path()→ Path parameter validation.Header()→ Header extraction.Depends()→ Dependency Injection.
Annotated consistently throughout the framework.
Annotated and Default Values
A common question is:
If Annotated moves the metadata inside the type annotation, where do default values go?
The answer is simple:
- Validation metadata goes inside
Annotated. - Default values are still assigned using
=.
Example 1: Field()
stris the type.Field(...)defines the validation rules."Engineering"is the default value.
Example 2: Query()
intis the type.Query(...)defines the validation rules.10is the default value.
Example 3: Header()
stris the type.Header()tells FastAPI to read the value from the request header."en"is the default value if the header is not provided.
Example 4: Depends()
Although less common, the same syntax applies.
General Rule
- Type → What kind of value is expected.
- Metadata → Validation or dependency information.
- DefaultValue → Used only when the value is optional.
Examples
Key Takeaway
Think of the syntax as three separate parts:- Type defines what the value is.
- Metadata defines how FastAPI or Pydantic should process it.
- DefaultValue defines what to use when the value is optional.