FastAPI
ASGI, валидация Pydantic, внедрение зависимостей, модели ответа, асинхронные эндпоинты и аутентификация по JWT.
15 вопросов
JuniorТеорияОчень частоЧто делает Depends() в сигнатуре эндпоинта FastAPI?
Что делает Depends() в сигнатуре эндпоинта FastAPI?
Он объявляет, что FastAPI обязан вызвать этот callable до эндпоинта и передать его результат внутрь. Собственные параметры зависимости разрешаются так же, поэтому у неё могут быть под-зависимости, а результат кэшируется до конца одного запроса.
Типичные ошибки
- ✗Считать, что зависимость нужно вызывать самому внутри обработчика
- ✗Ждать, что одна и та же зависимость отработает по разу на каждый параметр, а не один раз за запрос
- ✗Забывать, что зависимость может объявлять собственные параметры и под-зависимости
Уточняющие вопросы
- →Как заставить зависимость отработать повторно в рамках одного запроса?
- →Куда вешать зависимость, которая должна защищать каждый маршрут роутера?
JuniorТеорияОчень частоНа чём построен FastAPI и откуда на самом деле берётся его скорость?
На чём построен FastAPI и откуда на самом деле берётся его скорость?
Это тонкий слой поверх двух библиотек — Starlette для ASGI-маршрутизации и Pydantic для валидации — который запускает ASGI-сервер вроде uvicorn. Скорость даёт конкурентный асинхронный ввод-вывод плюс скомпилированное Rust-ядро Pydantic, а не сам Python.
Типичные ошибки
- ✗Называть FastAPI фреймворком «со всем в комплекте» — в нём нет ни ORM, ни админки, ни миграций
- ✗Считать, что скорость даёт код самого FastAPI, а не ASGI и Rust-ядро Pydantic
- ✗Забывать, что приложение запускает ASGI-сервер вроде
uvicorn— FastAPI сервером не является
Уточняющие вопросы
- →Что даёт
Starletteиз того, чего не даётPydantic? - →Почему один воркер
uvicornспособен обслуживать много параллельных запросов?
JuniorТеорияОчень частоЧто FastAPI делает с моделью библиотеки валидации Pydantic, объявленной как тело запроса?
Что FastAPI делает с моделью библиотеки валидации Pydantic, объявленной как тело запроса?
Он разбирает тело JSON, приводит и проверяет каждое поле по объявленным типам, затем передаёт в обработчик типизированный экземпляр. При несовпадении обработчик не запускается вовсе, а FastAPI отвечает 422 со списком ошибок по полям. Эта же модель формирует схему OpenAPI.
Типичные ошибки
- ✗Думать, что аннотации нужны только для документации и в рантайме не проверяются
- ✗Ждать, что сбой валидации проявится как
500, а не как422 - ✗Забывать, что обработчик получает экземпляр модели, а не сырой
dict
Уточняющие вопросы
- →На что указывает поле
locв теле ошибки422? - →Как отклонять лишние ключи, которых в модели нет?
JuniorТеорияОчень частоЧем FastAPI отличается от Django по тому, что даёт сам фреймворк?
Чем FastAPI отличается от Django по тому, что даёт сам фреймворк?
Django идёт «со всем в комплекте» — ORM, миграции, админка, аутентификация и шаблоны встроены и работают на синхронном ядре. FastAPI даёт только маршрутизацию, валидацию, внедрение зависимостей и OpenAPI на асинхронном ядре; ORM, миграции и админку выбираете вы.
Типичные ошибки
- ✗Ждать, что ORM, админка или инструмент миграций придут вместе с FastAPI
- ✗Считать, что Django вообще не умеет async — он поддерживает ASGI, но ядро ORM остаётся синхронным
- ✗Выбирать между ними по синтаксису, а не по тому, что реально нужно проекту
Уточняющие вопросы
- →Какие части Django остаются синхронными даже под ASGI-сервером?
- →Что придётся добавить к FastAPI, чтобы заменить админку Django?
MiddleТеорияОчень частоКак FastAPI выполняет эндпоинт async def в отличие от обычного def?
Как FastAPI выполняет эндпоинт async def в отличие от обычного def?
Эндпоинт async def выполняется прямо в цикле событий, поэтому блокирующий вызов внутри него останавливает все остальные запросы этого воркера. Обычный def уходит в ограниченный пул потоков, где блокировка безопасна, но конкурентность упирается в число потоков.
Типичные ошибки
- ✗Пометить эндпоинт как
async defи вызвать внутри синхронный клиент - ✗Считать, что у обычного
defнет предела конкурентности - ✗Думать, что одно лишь
asyncделает блокирующую библиотеку неблокирующей
Уточняющие вопросы
- →Что произойдёт, когда пул потоков для обычных
def-эндпоинтов исчерпан? - →Как выполнить один блокирующий вызов изнутри эндпоинта
async def?
JuniorТеорияЧастоКак FastAPI решает, что параметр — это путь, query или тело запроса?
Как FastAPI решает, что параметр — это путь, query или тело запроса?
По имени и типу. Если имя есть в пути маршрута — это path-параметр; иначе скалярный тип (int, str, bool, UUID) становится query-параметром, а модель Pydantic — телом запроса. Path(), Query() и Body() переопределяют это правило.
Типичные ошибки
- ✗Считать, что источник определяет порядок аргументов в сигнатуре
- ✗Ждать, что одиночный скаляр попадёт в тело без
Body(embed=True) - ✗Забывать, что имя, совпавшее с путём маршрута, всегда побеждает любой другой источник
Уточняющие вопросы
- →Как заставить одиночный скалярный аргумент попасть в тело JSON?
- →Что будет, если два параметра объявят одно и то же query-имя?
MiddleТеорияЧастоКогда встроенный помощник BackgroundTasks — неверный инструмент для отложенной работы?
Когда встроенный помощник BackgroundTasks — неверный инструмент для отложенной работы?
Он выполняет callable в том же процессе сразу после ответа — без сохранения, без повторов и без очереди. Поэтому он не подходит, когда работа тяжёлая или упирается в процессор, должна пережить перезапуск или требует повторов: эти случаи закрывает очередь на брокере вроде Celery или ARQ.
Типичные ошибки
- ✗Считать, что фоновая задача переживёт переезд или падение процесса
- ✗Класть туда работу, упирающуюся в процессор, и обесточивать воркер, в котором она идёт
- ✗Ждать автоматических повторов, когда задача бросает исключение
Уточняющие вопросы
- →Почему ресурс из зависимости с
yieldвнутри фоновой задачи уже закрыт? - →Что вы оставили бы в
BackgroundTasksдаже после перехода на Celery?
MiddleТеорияЧастоКак зависимость с подписанным токеном JWT защищает маршрут в FastAPI?
Как зависимость с подписанным токеном JWT защищает маршрут в FastAPI?
OAuth2PasswordBearer достаёт токен из заголовка Authorization: Bearer и даёт 401, если его нет. Ваша зависимость дальше проверяет подпись и exp, загружает пользователя и бросает 401 при сбое. Объявление через Depends и включает защиту, и документирует её.
Типичные ошибки
- ✗Декодировать содержимое токена, не проверив подпись
- ✗Забыть проверку
exp, из-за чего просроченный токен продолжает работать - ✗Считать, что токен
JWTбез состояния можно отозвать без списка отзыва или короткого срока жизни
Уточняющие вопросы
- →Как отозвать токен до наступления его
exp? - →Куда повесить защиту, чтобы она покрыла каждый маршрут роутера?
MiddleТеорияЧастоЧто response_model меняет в том, что реально получает клиент?
Что response_model меняет в том, что реально получает клиент?
Возвращённый объект валидируется и заново сериализуется через эту модель, поэтому любое поле, которого в модели нет, отбрасывается до отправки ответа. Именно это не даёт утечь password или внутреннему id, и это же задаёт схему ответа в документации.
Типичные ошибки
- ✗Считать
response_modelдокументацией и возвращать объекты, в которых остались секретные поля - ✗Ждать, что лишние поля пройдут насквозь, а не будут отброшены
- ✗Забывать, что ответ проверяется заново, поэтому плохое значение падает уже после отработавшего обработчика
Уточняющие вопросы
- →Какой код статуса даст ответ, не прошедший собственный
response_model? - →Когда
response_model_exclude_unsetизменит содержимое ответа?
MiddleТеорияЧастоПочему тело запроса с неверными типами даёт 422, а не 500?
Почему тело запроса с неверными типами даёт 422, а не 500?
Валидация идёт до обработчика, поэтому на сервере ничего не сломалось — неверны данные клиента. FastAPI ловит RequestValidationError встроенным обработчиком и отвечает 422 с записью loc/msg/type на каждое плохое поле. Ручная валидация внутри обработчика даёт уже 500.
Типичные ошибки
- ✗Считать
422общим кодом ошибки, а не специфичным для валидации - ✗Ждать, что модель
Pydantic, собранная руками внутри обработчика, тоже даст422 - ✗Думать, что валидация идёт внутри обработчика, а не до него
Уточняющие вопросы
- →Как заменить стандартное тело
422собственным форматом ошибки? - →Какое исключение обработать, чтобы логировать каждые отклонённые данные?
MiddleКодИногдаПереиспользуемая зависимость пагинации с проверкой границ
Переиспользуемая зависимость пагинации с проверкой границ
Объявите limit и offset обычными параметрами простой функции с границами Query(ge=..., le=...) и верните их. FastAPI разрешает собственные параметры зависимости ровно так же, как параметры эндпоинта, поэтому границы проверяются до запуска обработчика, а плохое значение даёт 422.
Типичные ошибки
- ✗Читать строку запроса руками вместо объявления параметров у зависимости
- ✗Молча подрезать значения вне диапазона вместо того, чтобы дать
422их отклонить - ✗Считать, что
geиleвQuery()нужны только документации
Уточняющие вопросы
- →Как раздать эту зависимость на каждый маршрут роутера?
- →Что изменится, если двум эндпоинтам нужны разные потолки
limit?
MiddleДизайнИногдаКоманда начинает новый сервис, который на каждый запрос обращается к четырём сторонним HTTP-API и сводит их ответы; каждый внешний вызов занимает 200-400 мс, а сервис должен держать несколько тысяч одновременных соединений на скромном железе. Ещё нужен внутренний экран бэк-офиса, где поддержка смотрит и правит записи, а модель данных за первый год будет меняться часто. Обоснуйте выбор между FastAPI и связкой Django плюс DRF. Разберите, какая модель конкурентности подходит для этого веера вызовов и почему, во что каждый вариант обойдётся для экрана бэк-офиса и для миграций схемы и что придётся строить самим при выбранном варианте.
Команда начинает новый сервис, который на каждый запрос обращается к четырём сторонним HTTP-API и сводит их ответы; каждый внешний вызов занимает 200-400 мс, а сервис должен держать несколько тысяч одновременных соединений на скромном железе. Ещё нужен внутренний экран бэк-офиса, где поддержка смотрит и правит записи, а модель данных за первый год будет меняться часто. Обоснуйте выбор между FastAPI и связкой Django плюс DRF. Разберите, какая модель конкурентности подходит для этого веера вызовов и почему, во что каждый вариант обойдётся для экрана бэк-офиса и для миграций схемы и что придётся строить самим при выбранном варианте.
FastAPI: нагрузка — это веер вызовов с ожиданием ввода-вывода, и один асинхронный воркер держит тысячи ждущих внешних вызовов, под каждый из которых синхронному ядру Django понадобился бы поток или процесс. Платите тем, что админка и миграции не входят в комплект — вы берёте SQLAlchemy с Alembic и строите или покупаете бэк-офис сами, а это разовая ограниченная цена против постоянного выигрыша в пропускной способности.
Типичные ошибки
- ✗Называть веер внешних вызовов нагрузкой на процессор и считать воркеры по числу ядер
- ✗Считать, что ORM Django асинхронна под ASGI и не требует потока на запрос
- ✗Не учитывать, что экран бэк-офиса и миграции — это реальная работа, которую вы берёте на себя с FastAPI
Уточняющие вопросы
- →Что сломается первым, если оставить Django и просто поднять число воркеров?
- →Какие части экрана бэк-офиса вы отказались бы писать руками?
SeniorДебаггингИногдаОдин медленный эндпоинт тормозит все параллельные запросы — найдите причину
Один медленный эндпоинт тормозит все параллельные запросы — найдите причину
requests — синхронный клиент, поэтому вызов блокирует единственный цикл событий воркера на весь поход к внешнему сервису, и /health в это время обслужить нечем. Затем render_pdf, упирающийся в процессор, борется за интерпретатор в том же процессе уже после ответа. Лечение: асинхронный клиент для вызова и очередь на брокере для отрисовки.
Типичные ошибки
- ✗Считать, что FastAPI сам уводит синхронный клиент в пул потоков внутри
async def - ✗Принимать рост задержек на посторонних маршрутах за сетевую проблему, а не за заблокированный цикл
- ✗Считать, что фоновые задачи идут в отдельном процессе и работа под процессор там безопасна
Уточняющие вопросы
- →Почему добавление процессов-воркеров прячет симптом, но не устраняет причину?
- →Что вы измерите, чтобы доказать, что узкое место — цикл событий, а не внешний сервис?
SeniorТеорияИногдаКогда teardown зависимости с yield выполняется относительно ответа?
Когда teardown зависимости с yield выполняется относительно ответа?
После того как ответ отправлен, и до любой фоновой задачи. Поэтому нельзя бросить там HTTPException, чтобы изменить уже полученный клиентом ответ, а сессия, отданная обработчику, внутри фоновой задачи уже закрыта — задача должна открыть свою.
Типичные ошибки
- ✗Бросать
HTTPExceptionпослеyieldи ждать, что клиент это увидит - ✗Использовать внутри фоновой задачи сессию, отданную зависимостью
- ✗Считать, что teardown отрабатывает до записи ответа
Уточняющие вопросы
- →Как оставить сессию открытой для отложенной работы, не допустив утечки?
- →Что происходит с teardown, когда исключение бросает сам обработчик?
SeniorПроизводительностьИногдаВо что обходится response_model на горячем списочном эндпоинте с 5000 строк?
Во что обходится response_model на горячем списочном эндпоинте с 5000 строк?
Каждая строка валидируется и сериализуется повторно: объекты ORM уже собраны, а модель пересобирает их по полям до записи JSON. На больших списках этот проход и определяет время запроса. Сузьте модель, разбейте выдачу на страницы или верните объект ответа напрямую, минуя модель.
Открыть задачу →Типичные ошибки
- ✗Считать
response_modelразовой ценой при старте, а не ценой на каждую строку - ✗Менять кодировщик JSON на более быстрый, оставив второй проход валидации на месте
- ✗Отдавать неограниченные списки и трактовать результат как проблему сериализации
Уточняющие вопросы
- →Как доказать, что запрос определяет проход валидации, а не обращение к базе?
- →Чем вы жертвуете, возвращая объект ответа напрямую?