Как Pydantic интегрируется с FastAPI для валидации запросов и ответов?
FastAPI автоматически валидирует тело запроса через Pydantic-модели, возвращает 422 при ошибках и сериализует ответы через response_model, отфильтровывая лишние поля. OpenAPI-схема генерируется из Field-аннотаций без дополнительного кода.
Как Pydantic интегрируется с FastAPI
FastAPI использует Pydantic v2 как основной механизм валидации и сериализации. Интеграция работает на трёх уровнях: тело запроса, параметры пути/запроса и тело ответа.
Валидация входящих запросов
Когда FastAPI получает HTTP-запрос, он автоматически парсит тело и передаёт данные в Pydantic-модель. При ошибке возвращается 422 Unprocessable Entity с детализированным JSON-ответом.
from fastapi import FastAPI
from pydantic import BaseModel, Field, EmailStr
app = FastAPI()
class UserCreate(BaseModel):
name: str = Field(min_length=2, max_length=100)
email: EmailStr
age: int = Field(ge=18, le=120)
class UserResponse(BaseModel):
id: int
name: str
email: str
model_config = {"from_attributes": True}
@app.post("/users", response_model=UserResponse, status_code=201)
async def create_user(body: UserCreate):
# body уже провалидирован Pydantic
# body.email — нормализованный lowercase адрес
user = await db.create_user(body.name, body.email, body.age)
return user # SQLAlchemy ORM-объект → Pydantic через from_attributes
Параметры пути и строки запроса
Pydantic валидирует и приводит типы для path/query параметров. FastAPI использует Query(), Path() с теми же Pydantic-ограничениями.
from fastapi import Query, Path
@app.get("/users/{user_id}")
async def get_user(
user_id: int = Path(ge=1),
include_deleted: bool = Query(default=False),
page: int = Query(default=1, ge=1),
size: int = Query(default=20, ge=1, le=100),
):
...
Сериализация ответов через response_model
Параметр response_model указывает FastAPI прогнать возвращаемый объект через Pydantic перед отправкой клиенту. Это фильтрует лишние поля (например, password_hash) и приводит типы к JSON-совместимым.
class UserPublic(BaseModel):
id: int
name: str
# email и age не включены — они не попадут в ответ
@app.get("/users/{user_id}", response_model=UserPublic)
async def get_user(user_id: int):
return await db.get_user(user_id) # объект может содержать email, но в ответ не уйдёт
OpenAPI-схема генерируется автоматически
FastAPI читает аннотации Pydantic-моделей и строит OpenAPI-схему (/docs, /openapi.json) без дополнительного кода. Field(description=..., example=...) появляется в Swagger UI.
Обработка ошибок валидации
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
return JSONResponse(
status_code=422,
content={"detail": exc.errors(), "body": exc.body},
)
Подводные камни
- from_attributes не включён по умолчанию — если вернуть ORM-объект без
model_config = {"from_attributes": True}, Pydantic выброситValidationErrorвместо сериализации атрибутов объекта. - Ленивые отношения SQLAlchemy — при
from_attributes=TruePydantic попытается обратиться к lazy-loaded relationship уже за пределами сессии, что даётMissingGreenletилиDetachedInstanceError. Решение:selectinload/joinedloadили явныйmodel_validate(obj, from_attributes=True)внутри сессии. - response_model_exclude_unset — по умолчанию в ответ включаются все поля модели, даже если они не заданы. Используйте
response_model_exclude_unset=Trueдля PATCH-эндпоинтов, чтобы не перезаписывать поля значениями по умолчанию. - Вложенные модели и глубокая валидация — FastAPI валидирует рекурсивно, поэтому большие вложенные структуры могут давать длинные трассировки ошибок; нужно явно указывать понятные
Field(description=...). - Pydantic v1 vs v2 — FastAPI ≥0.100 требует Pydantic v2. Если в проекте есть legacy-код с
orm_mode = True, нужно мигрировать наfrom_attributes = Trueвmodel_config. - Circular imports — вынос Pydantic-моделей в отдельный модуль
schemas.pyпредотвращает циклические импорты между роутерами и моделями. - Производительность при больших списках —
response_model=list[UserResponse]для 10 000 объектов даёт заметный оверхед валидации. Альтернатива:response_model_includeили прямойJSONResponseсmodel.model_dump().
Common mistakes
- Описывать fastapi integration только как термин и не показывать механизм на минимальном примере.
- Игнорировать ошибки, пустые данные, конкурентный доступ или границы транзакции.
- Не связывать поведение с официальным контрактом Pydantic и реальной эксплуатацией.
What the interviewer is testing
- Объясняет fastapi integration через последовательность действий, а не через набор ключевых слов.
- Приводит короткий кодовый пример или production-сценарий с ожидаемым поведением.
- Называет хотя бы один риск: производительность, безопасность, транзакции, память или сопровождение.
- Умеет обсудить отказ, наблюдаемость и rollback без изменения публичного контракта.