Программирование с ИИ
ENRU
7.3

API, схемы и валидация

граница, на которой проверяют всё

Что это

API — набор адресов, по которым с твоим сервером можно разговаривать. Схема — описание того, какие данные приходят и уходят. Валидация — проверка, что пришедшее соответствует схеме.

Для работы с ИИ это самое важное место бэкенда: граница, где данные приходят снаружи, — единственное место, где их ещё можно проверить. Пропустил здесь — дальше неверные данные расходятся по всему приложению.

Как выглядит в коде

from pydantic import BaseModel, Field


class SubIn(BaseModel):
    city: str = Field(min_length=2, max_length=50)


class SubOut(BaseModel):
    user_id: int
    cities: list[str]


@app.post("/subs/{user_id}", response_model=SubOut, status_code=201)
def add_sub(user_id: int, body: SubIn) -> SubOut:
    cities = SUBS.setdefault(user_id, [])
    if body.city in cities:
        raise HTTPException(status_code=409, detail="уже подписан")
    cities.append(body.city)
    return SubOut(user_id=user_id, cities=cities)

Что происходит при неверных данных:

curl -X POST http://127.0.0.1:8765/subs/7 \
     -H "Content-Type: application/json" -d '{"city":"X"}'
измерено
[422]
{"detail":[{"type":"string_too_short","loc":["body","city"],
 "msg":"String should have at least 2 characters",
 "input":"X","ctx":{"min_length":2}}]}

Обработчик не запустился вообще. Проверка сработала до него, ответ описывает ровно то, что не так и где.

Почему это та же мысль, что и типы

Вернись к статье 6.3: mypy ловит ошибки до запуска. Схема делает то же самое на границе сервера — ловит неверные данные до обработчика.

Разница в том, где проходит граница:

ИнструментЧто проверяетКогда
mypyтвой код согласован сам с собойдо запуска
Схема запросаданные снаружи соответствуют ожиданиямдо обработчика
Схема ответаты отдал то, что обещалдо отправки

Три разных рубежа, одна идея: описать форму данных один раз и дать машине проверять.

Что писать в схеме

Не только типы, но и ограничения:

class SubIn(BaseModel):
    city: str = Field(min_length=2, max_length=50)
    notify_at: int = Field(ge=0, le=23)          # час от 0 до 23
    email: EmailStr | None = None                 # формат проверяется

Каждое ограничение в схеме — это проверка, которую не придётся писать руками и о которой не забудет ИИ.

Адреса

Короткие правила, которых хватает:

ПравилоПример
Существительные во множественном числе/subs, /users
Действие выражает метод, а не адресDELETE /subs/7, не /deleteSub
Вложенность — через путь/users/7/subs
Фильтры — через строку запроса/subs?city=Oslo&limit=10

Как сказать ИИ

Запрос
Опиши схему запроса и ответа для POST /subs/{user_id}:
город — строка от 2 до 50 символов, час уведомления —
число от 0 до 23, необязательное. В ответе: user_id
и список городов. Код обработчика пока не пиши.

Схема как постановка задачи — то же, что сигнатура в статье 6.5: короче слов и проверяется машиной.

Запрос
Проверь, что в response_model не попадают внутренние поля:
хеши паролей, служебные id, пометки. Перечисли, что отдаётся
клиенту.
Запрос
Какие ограничения стоит добавить в эту схему, чтобы кривые
данные не доходили до обработчика? Только ограничения,
логику не трогай.

На что смотреть

Валидация внутри обработчика. if len(city) < 2: raise ... — работает, но проверка размазывается по коду, и часть случаев забывается. Место проверок — схема.

dict вместо схемы. def add_sub(body: dict) отключает проверку целиком: придёт что угодно.

Нет response_model. Отдаётся всё, что вернул обработчик.

Схема есть, ограничений нет. city: str без длины примет и пустую строку, и строку на мегабайт.

Разные схемы для одного и того же. Три слегка отличающихся описания подписки в трёх местах — рассинхронизируются при первой же правке.

Шпаргалка

  • Граница сервера — единственное место, где внешние данные ещё можно проверить.

  • Схема описывает форму, валидация проверяет, фреймворк делает это сам.

  • При неверных данных обработчик не запускается вообще.

  • response_model защищает от случайной отдачи лишних полей.

  • Ограничения (min_length, ge, le) — проверки, которые не забудутся.

  • Схема в запросе к ИИ работает как сигнатура: точнее слов.