Сборка API-воркера на FastAPI
Порядок работы
- Извлечь заданные runtime-требования и изучить существующую точку сборки.
- Отделить фабрику ASGI-приложения от запуска процесса.
- Определить минимальные неизменяемые параметры и зависимости фабрики.
- Собрать типизированный runtime context через lifespan.
- Зарегистрировать заданные routers, handlers и middleware в явном порядке.
- Добавить отдельный entrypoint и настроить режим Uvicorn и graceful shutdown.
- Проверить startup, shutdown, entrypoint, ошибки, health endpoints и middleware.
Не спрашивать повторно о поведении, однозначно заданном требованиями или кодом. Задать вопрос только при противоречии, небезопасном решении или выборе, меняющем публичное поведение.
Граница ответственности
Скил реализует сборку и жизненный цикл HTTP API-процесса. Он не определяет:
- пути, методы и внешние request/response/error-контракты;
- transport-модели и преобразования в application-контракты;
- бизнес-правила, транзакционные границы и Unit of Work;
- конкретные persistence, broker и другие исходящие адаптеры;
- структуру и источники конфигурации;
- фоновые циклы и message consumers.
Не импортировать domain-ошибки и конкретные infrastructure-реализации в presentation. Получать application entry points, фабрики ресурсов и валидированные параметры из composition root.
Архитектура
Разделить три роли:
configuration -> composition root -> application factory -> ASGI application
|
+-----------------------> Uvicorn runner
- Composition root преобразует внешнюю конфигурацию в минимальные параметры, выбирает реализации адаптеров и связывает зависимости.
- Application factory создаёт FastAPI/ASGI-приложение без I/O и открытия соединений.
- Lifespan создаёт process-scoped ресурсы и runtime context.
- Runner запускает приложение, но не определяет его маршруты и зависимости.
В новом сервисе использовать APIWorker как стандартное имя process-level
сборщика и runner-а. Имена фабрики приложения и runtime context адаптировать к
существующим терминам проекта. При правке существующего сервиса не выполнять
механическое переименование эквивалентных ролей без отдельной задачи.
Подробный вариант: фабрика приложения.
Entrypoint API-процесса
В новом сервисе использовать структуру этого репозитория:
entrypoints/api.pyсодержит import-safemain()и защитуif __name__ == "__main__";main()загружаетAPIWorkerSettings, выполняет startup preflight и вызываетAPIWorker(settings).run();presentation/api/server.pyсодержитAPIWorker, который собирает приложение и настраивает Uvicorn;- фабрика приложения, lifespan, routers и middleware не размещаются в entrypoint;
- импорт entrypoint не читает конфигурацию, не открывает ресурсы, не применяет миграции и не запускает сервер.
В существующем проекте сохранять эквивалентную точку входа, если переименование не входит в задачу.
Конфигурируемые параметры
Все изменяемые между окружениями параметры получать снаружи:
- host, port, event loop и параметры graceful shutdown;
- количество workers или режим reload;
- root path, OpenAPI и адреса интерфейсов документации;
- CORS, trusted hosts и proxy trust;
- включение и параметры middleware;
- пути health endpoints.
Не передавать всему presentation общий settings-объект. Преобразовать его в неизменяемые типизированные параметры API и runner-а. Не читать env/YAML и не создавать module-level settings instance в presentation.
Не передавать model_dump() в FastAPI, Uvicorn или middleware. Сопоставлять
каждый параметр с актуальным аргументом клиента явно.
Фабрика приложения
Фабрика:
- принимает параметры API, lifespan factory, routers, error handlers и middleware specifications;
- возвращает новое приложение при каждом вызове;
- не открывает соединения и не выполняет сетевые проверки;
- регистрирует компоненты детерминированно;
- не импортирует глобальный
main_routerи конкретные адаптеры; - пригодна для тестов, import string и Uvicorn factory mode.
Проверять уникальность route names и operation IDs, а также порядок статических и параметризованных путей, если это требуется существующей маршрутизацией.
Жизненный цикл и контекст выполнения
Использовать один lifespan async context manager. Не смешивать его с
startup/shutdown handlers.
- Создавать обязательные ресурсы до начала приёма запросов.
- Регистрировать освобождение каждого ресурса в
AsyncExitStackсразу после успешного создания. - При частично неуспешном startup закрывать уже созданные ресурсы.
- На shutdown освобождать ресурсы в обратном порядке.
- Не подавлять startup-ошибку или отмену задачи.
- Не запускать бесконечные background loops внутри API lifespan.
Хранить в app.state один неизменяемый типизированный runtime context. Контекст
содержит application entry points, readiness state и только разрешённые
shared-ресурсы через абстрактные контракты. Не помещать туда Unit of Work,
request-scoped объекты или общий settings.
Подробности: lifespan и контекст.
Промежуточное ПО
Состав middleware брать из требований. Для каждой middleware зафиксировать:
- какие scope types она обрабатывает;
- какие данные создаёт и кто их потребляет;
- видит ли она ответы и исключения downstream;
- её место во входящем и исходящем пути;
- безопасный набор логируемых полей.
Для собственных сквозных middleware предпочитать pure ASGI. Использовать
BaseHTTPMiddleware только если его ограничения, включая propagation
contextvars, приемлемы.
Middleware должна быть stateless: изменяемое состояние хранить локально на
вызов, а конфигурацию задавать в __init__.
CORS подключать только по требованиям. Если CORS должен присутствовать и на
ответах необработанных ошибок, обернуть им всё приложение. Не заменять отсутствие
решения разрешающей политикой *.
Подробности: middleware.
Ошибки и логирование
Presentation обрабатывает:
- публичные application-ошибки;
- транспортные ошибки FastAPI/Pydantic;
- непредвиденные исключения на внешней error boundary.
Не обрабатывать domain-ошибки напрямую. Формат ответа и mapping статусов брать из HTTP-требований, а не из полей внутреннего исключения.
- Не возвращать stack trace, внутреннее исключение и технические детали.
- Применять
python-service-logging-writingи logging-профиль HTTP API. - Создавать request context на внешней middleware до вызова downstream и очищать
его в
finally, включая disconnect, timeout, cancellation и исключение. - Итоговую запись формировать после получения ответа либо безопасного error response; измерять полную длительность монотонными часами.
- Непредвиденное исключение логировать ровно один раз с
exc_infoна общей error boundary. Итоговая middleware не должна добавлять второй stack trace. - Передавать из error handlers безопасную классификацию результата, не извлекая внутренние исключения повторно.
- Отключать или перенастраивать Uvicorn access/error logs, если они дублируют согласованную итоговую запись или stack trace.
- Ожидаемые ошибки логировать только на предусмотренном уровне.
- Передавать request ID через logging context.
- Не логировать authorization, cookies, секреты и произвольные тела.
- Не логировать успешные healthcheck, если профиль прямо этого не требует.
Подробности: обработка ошибок.
Проверки состояния
Добавлять проверки только по требованиям:
- liveness подтверждает работу процесса без вызовов внешних систем;
- readiness показывает способность принимать трафик после успешного startup;
- readiness читает дешёвое состояние ресурсов, а не запускает тяжёлую диагностику;
- ответы не раскрывают адреса зависимостей, пути секретов и исключения.
Подробности: health checks.
Запуск Uvicorn
Выбрать один режим:
- готовый объект приложения и программный
uvicorn.Server— один процесс без reload; - import string или application factory — reload либо несколько workers.
Не сочетать готовый объект приложения с параметрами, требующими повторного импорта или создания приложения в дочернем процессе. Каждый worker создаёт собственные ресурсы через lifespan.
Доверять forwarded headers только от согласованных proxy IPs. root_path,
OpenAPI/docs URLs и proxy trust передавать как явные параметры. Не выводить
scheme, host или client IP из недоверенных заголовков.
Подробности: запуск Uvicorn.
Плавное завершение
- Прекратить приём новых запросов.
- Дать активным запросам конфигурируемое время на завершение.
- После drain закрыть lifespan-ресурсы.
- Продолжать попытку закрытия остальных ресурсов при ошибке отдельного cleanup.
- Сделать cleanup идемпотентным, где повторный вызов возможен.
- Не подавлять cancellation.
Тестирование
Для wiring-кода не требовать изолированные unit-тесты, если это запрещено правилами проекта. Интеграционно проверить:
- успешный и частично неуспешный startup;
- освобождение ресурсов и обратный порядок shutdown;
- runtime context;
- порядок и эффекты заданных middleware;
- безопасные application и unexpected error responses;
- отсутствие двойного логирования;
- очистку и изоляцию request context для параллельных запросов;
- обязательные поля, уровни и события logging-профиля;
- liveness/readiness;
- CORS только при его включении;
- сборку выбранного режима Uvicorn без открытия production-порта.
- вызов
main()с подменёнными loader, preflight иAPIWorkerбез открытия порта.
Антипаттерны
- Создание адаптеров внутри presentation по конкретным классам.
- Передача Unit of Work в endpoint.
- I/O при импорте или создании FastAPI application.
- Module-level application с уже открытыми ресурсами.
- Общий settings в
app.state. - Набор неописанных динамических полей
app.state. - Одновременное использование lifespan и event handlers.
- Обязательный CORS или разрешающий wildcard без требований.
- Подавление исключений middleware.
- Двойное логирование одной ошибки.
BaseHTTPMiddlewareбез оценки ограничений.- Готовый
appвместе с reload/multi-worker режимом.
Критерии готовности
- Фабрика приложения отделена от runner-а и не выполняет I/O.
- Зависимости поступают из composition root и не нарушают инверсию.
- Runtime context типизирован и создаётся только в lifespan.
- Частичный startup и shutdown безопасно освобождают ресурсы.
- Middleware и их порядок соответствуют требованиям.
- Ошибки преобразуются безопасно и логируются один раз.
- Режим Uvicorn совместим со способом передачи приложения.
- Отдельный import-safe entrypoint доводит сборку до
APIWorker.run(). - Health, proxy, CORS и OpenAPI не включены неявно.
- Согласованные проверки проходят.
Материалы
- Фабрика приложения
- Жизненный цикл и контекст выполнения
- Промежуточное ПО
- Обработка ошибок
- Запуск Uvicorn
- Проверки состояния
- Чеклист