BaseModel vs TypeAdapter
In Pydantic v2, you no longer need to solve every validation problem by creating a `BaseModel`. `BaseModel` is strong when you need a named schema contract, but `TypeAdapter` is often the better tool for validating arbitrary typed structures such as collections, unions, and `TypedDict`s.
Quick takeaway: if you need a named public contract, start with `BaseModel`. If you simply need to validate or serialize a type structure right now, reach for `TypeAdapter` first.
Compare the Roles Directly
| Question | BaseModel | TypeAdapter |
|---|---|---|
| Named field contract needed? | Strong | Weak |
| Model config and methods needed? | Strong | None |
| One-off validation of an arbitrary type? | Often too much | Strong |
Validate TypedDict, list[T], dict[K, V] directly? | May need wrapper model | Direct |
| Generate JSON schema? | Yes | Yes |
| Reuse compiled validation for one typed shape? | Via model class | Direct on the adapter |
When BaseModel Is the Right Tool
from datetime import datetime
from uuid import UUID
from pydantic import BaseModel, ConfigDict
class UserResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: UUID
email: str
created_at: datetime- API request and response DTOs
- settings objects
- named boundary contracts
When TypeAdapter Is Better
from typing import TypedDict
from pydantic import TypeAdapter
class Row(TypedDict):
id: int
name: str
row_adapter = TypeAdapter(list[Row])
rows = row_adapter.validate_python(
[{"id": "1", "name": "kim"}, {"id": 2, "name": "lee"}]
)
print(rows)
print(row_adapter.json_schema())If all you want is validation for `list[TypedDict]`, a wrapper model can be more ceremony than value. `TypeAdapter` keeps the code closer to the real typed shape.
Good Real-World Uses for TypeAdapter
- queue or Kafka payload validation
- validating shaped projections from ORM queries
- internal helper functions that only need typed data validation
- reusable constrained primitive or collection types
Checklist
Is this a public named contract?
If documentation and explicit fields matter, `BaseModel` is usually the natural fit.
Is this mostly a validation utility?
If the core need is validating an arbitrary type structure, `TypeAdapter` is usually cleaner.
Is the type deeply nested?
Complex generic shapes are often clearer as a direct adapter target than as a stack of wrapper models.
Is this a FastAPI boundary?
FastAPI request and response schemas usually align better with `BaseModel`, while internal helpers can use `TypeAdapter` freely.