Валидация входных данных в FastAPI через Pydantic: пошаговая инструкция
Настроим валидацию запросов в FastAPI с помощью Pydantic-моделей — от простой схемы до вложенных объектов и кастомных ошибок. На выходе получится API, который сам проверяет данные и возвращает понятные сообщения клиенту.

Что получится и сколько времени займёт
За 30–40 минут вы соберёте небольшой API на FastAPI 0.115.x и Pydantic v2, который принимает JSON-запрос, валидирует каждое поле — тип, длину, диапазон, формат e-mail — и при ошибке возвращает структурированный JSON с описанием проблемы. Никаких ручных if-проверок: всё делает Pydantic-модель, а FastAPI подхватывает результат автоматически.
Что нужно до начала
- Python 3.10 или новее.
- Установленные пакеты
fastapiиuvicorn. Pydantic v2 ставится как зависимость FastAPI, отдельно тянуть не нужно. - Любой HTTP-клиент для тестирования: встроенный Swagger UI (
/docs),curlили HTTPie. - Редактор кода и терминал.
Установка одной командой:
pip install "fastapi[standard]"
После установки выполните python -c "import fastapi; print(fastapi.__version__)" — должна появиться версия 0.115.x или выше. Если версия ниже 0.100, Pydantic v2 не поддерживается — обновите пакет.
Шаг 1. Минимальное приложение с моделью
Создайте файл main.py:
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI()
class UserCreate(BaseModel):
name: str
age: int
email: EmailStr # требует пакет email-validator
@app.post("/users")
async def create_user(user: UserCreate):
# user уже провалидирован к этому моменту
return {"status": "ok", "user": user.model_dump()}
Для работы EmailStr нужен дополнительный пакет:
pip install email-validator
Запустите сервер:
uvicorn main:app --reload
Проверка: откройте http://127.0.0.1:8000/docs — в Swagger UI появится эндпоинт POST /users со схемой запроса. Отправьте валидный JSON — получите 200. Отправьте {"name": 123, "age": "abc", "email": "not-email"} — получите 422 Unprocessable Entity с массивом ошибок.
Шаг 2. Ограничения на поля
Голый тип — слабая проверка. Добавим ограничения через Field:
from pydantic import BaseModel, EmailStr, Field
class UserCreate(BaseModel):
name: str = Field(
min_length=2,
max_length=50,
examples=["Анна"],
)
age: int = Field(
ge=18, # greater or equal
le=120,
)
email: EmailStr
Проверка: отправьте {"name": "A", "age": 17, "email": "a@b.com"}. Ответ содержит две ошибки: name короче двух символов, age меньше 18.
Параметры ge, le, gt, lt работают для любых числовых типов, включая float и Decimal. Для строк — min_length, max_length, pattern (регулярное выражение).
Шаг 3. Вложенные модели и списки
Реальные запросы редко бывают плоскими. Допустим, пользователь передаёт список адресов:
class Address(BaseModel):
city: str = Field(min_length=1)
zip_code: str = Field(pattern=r"^\d{6}$") # шесть цифр
class UserCreate(BaseModel):
name: str = Field(min_length=2, max_length=50)
age: int = Field(ge=18, le=120)
email: EmailStr
addresses: list[Address] = Field(min_length=1, max_length=5)
Pydantic рекурсивно валидирует каждый элемент списка. Если во втором адресе невалидный zip_code, ответ укажет путь body -> addresses -> 1 -> zip_code.
Проверка: отправьте запрос с пустым массивом addresses: [] — получите ошибку min_length.
Шаг 4. Кастомный валидатор
Иногда одних аннотаций недостаточно. Например, имя не должно состоять из одних пробелов:
from pydantic import field_validator
class UserCreate(BaseModel):
name: str = Field(min_length=2, max_length=50)
age: int = Field(ge=18, le=120)
email: EmailStr
addresses: list[Address] = Field(min_length=1, max_length=5)
@field_validator("name")
@classmethod
def name_must_not_be_blank(cls, value: str) -> str:
if not value.strip():
raise ValueError("Имя не может состоять только из пробелов")
return value.strip()
Декоратор @field_validator — это API Pydantic v2. В Pydantic v1 использовался @validator, и сигнатура отличалась. Если вы работаете с FastAPI версии ниже 0.100, синтаксис будет другим — сверьтесь с документацией Pydantic той версии, которая у вас установлена.
Проверка: отправьте {"name": " ", ...} — получите 422 с текстом Имя не может состоять только из пробелов.
Шаг 5. Человекопонятные ошибки
Стандартный ответ FastAPI на ошибку валидации выглядит так:
{
"detail": [
{
"type": "string_too_short",
"loc": ["body", "name"],
"msg": "String should have at least 2 characters",
"input": "A"
}
]
}
Формат подробный, но английские сообщения не всегда подходят. Можно перехватить обработчик:
from fastapi import Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(
request: Request,
exc: RequestValidationError,
) -> JSONResponse:
errors = []
for err in exc.errors():
errors.append({
"field": " -> ".join(str(loc) for loc in err["loc"]),
"message": err["msg"],
})
return JSONResponse(
status_code=422,
content={"errors": errors},
)
Проверка: невалидный запрос теперь возвращает компактный JSON с полями field и message.
Pydantic-модель — это контракт между клиентом и сервером. Чем точнее контракт описан в модели, тем меньше проверок размазано по бизнес-логике и тем проще менять и то, и другое по отдельности.
Шаг 6. Модель ответа
Валидация работает и на выход. Укажите response_model, чтобы FastAPI отфильтровал лишние поля и проверил типы перед отправкой:
class UserOut(BaseModel):
name: str
email: EmailStr
@app.post("/users", response_model=UserOut)
async def create_user(user: UserCreate):
# даже если внутри появится поле password, клиент его не увидит
return user
Проверка: в ответе присутствуют только name и email, поля age и addresses отсечены.
Сводка: что за что отвечает
| Механизм | Что делает | Где описывается |
|---|---|---|
Аннотация типа (int, str, EmailStr) | Проверяет тип и базовый формат | Поле модели |
Field(ge=..., max_length=...) | Ограничивает диапазон и длину | Аргументы Field |
Вложенная модель / list[Model] | Рекурсивная валидация структуры | Тип поля |
@field_validator | Произвольная логика для одного поля | Метод модели |
@model_validator | Кросс-полевая проверка (например, «дата начала раньше даты конца») | Метод модели |
response_model | Фильтрация и валидация ответа | Декоратор эндпоинта |
@model_validator — декоратор Pydantic v2, который получает доступ ко всем полям модели сразу. Полезен, когда корректность одного поля зависит от другого. Подробности — в документации Pydantic.
Если не получилось
ModuleNotFoundError: No module named 'email_validator'— не установлен пакетemail-validator. Выполнитеpip install email-validator.PydanticUserError: ... @validator ... is not supported in Pydantic v2— вы используете декоратор@validatorиз Pydantic v1. Замените на@field_validatorи добавьте@classmethod.- Поля приходят
nullвместо ошибки — скорее всего, поле объявлено с дефолтным значениемNone:name: str | None = None. Уберите= None, если поле обязательное. 422без тела ответа — кастомный обработчикRequestValidationErrorупал с исключением. Проверьте, чтоexc.errors()вызывается как метод, а не как атрибут.- Swagger UI не показывает схему — убедитесь, что модель используется именно как аннотация параметра функции, а не передаётся вручную через
Request.json().
Что дальше
Валидация входных данных — первый рубеж, но не единственный. Следующие шаги:
- Подключить
@model_validator(mode="after")для кросс-полевых проверок — например, чтоpasswordиpassword_confirmсовпадают. - Вынести модели в отдельный модуль
schemas.py, чтобы не смешивать контракты с логикой маршрутов. - Настроить
model_config(бывшийConfig) — например,str_strip_whitespace=True, чтобы автоматически обрезать пробелы у всех строковых полей. - Посмотреть, как Pydantic генерирует JSON Schema — она же используется для автодокументации в Swagger UI и может экспортироваться для клиентских генераторов.
Pydantic v2 — библиотека валидации и сериализации данных для Python. Ядро написано на Rust (пакет pydantic-core), что даёт заметный прирост скорости по сравнению с v1. FastAPI использует Pydantic как основной механизм работы со схемами запросов и ответов.
Карточки для маркетплейсов: Таблица товаров и папка фотографий на входе — комплект слайдов и описаний на выходе, по требованиям Wildberries и Ozon.
Спросить за 5 ₽Источники: FastAPI — Request Body (официальная документация), Pydantic v2 — Validators (официальная документация)
Частые вопросы
Обязательно ли использовать Pydantic, или можно валидировать данные вручную?
Что будет, если клиент пришлёт лишние поля, которых нет в модели?
У меня FastAPI ниже 0.100 и Pydantic v1 — код из статьи заработает?
Как валидировать query-параметры и заголовки, а не только тело запроса?
Кастомный валидатор выбрасывает ValueError, но клиент получает 500 вместо 422. В чём причина?
Можно ли возвращать ошибки валидации на русском языке?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.