Skip to content

Адаптеры фреймворков

Каждый адаптер создаёт ровно один scope на запрос, кладёт его в нативное для фреймворка место и освобождает в безопасной точке жизненного цикла. При этом сохраняется конкретный тип контейнера, которым владеет приложение: request.di остаётся полностью типизированным, а не any и не базовым контейнером.

Это вся их работа. Адаптеры - тонкая обвязка жизненного цикла: тот же дизайн, который оставляет @inferdi/inferdi без зависимостей, не добавляет в core декораторы, сканирование контроллеров, injection параметров обработчика и поиск маршрутов. Вы подключаетесь к жизненному циклу запроса во фреймворке, а не к его представлению о dependency injection.

Пакеты

ПакетФреймворкГде хранится scopeРежим без scope запроса
@inferdi/fastifyFastify v5request.diда
@inferdi/honoHono v4c.var.diнет
@inferdi/koaKoa v3ctx.state.diнет
@inferdi/expressExpress 5req.diнет
@inferdi/elysiaElysia v1context.diда

Общий контракт жизненного цикла

В режиме со scope каждый адаптер проходит одни и те же шаги на каждый запрос:

  1. Создать - создаёт scope из корневого контейнера (createScope, по умолчанию root.createScope()) в начале запроса.
  2. Выставить - кладёт его в нативное место фреймворка (request.di, ctx.state.di, c.var.di или ключ контекста Elysia) до setup, чтобы и ошибка setup, и ваши cleanup-хуки видели один и тот же слот.
  3. Настроить - setupScope наполняет scope данными запроса: идентификатор запроса, авторизованный пользователь, IP клиента. Может быть асинхронным.
  4. Обработать - обработчики маршрутов и ошибок фреймворка резолвят сервисы из выставленного scope.
  5. Очистить - освобождает scope в безопасной точке завершения жизненного цикла (disposeScope, по умолчанию scope.dispose()), если владение не было передано приложению.

Общие опции

ОпцияПо умолчаниюНазначение
containerобязательнаКорневой контейнер, доступный приложению. Адаптеры его не очищают, кроме opt-in disposeRootOnClose у Fastify.
createScoperoot.createScope()Создание scope на запрос. Может быть асинхронным.
setupScopeнетНаполнение scope до обработчиков. Может быть асинхронным.
disposeScopescope.dispose()Пользовательская очистка. Синхронная или асинхронная.
autoDisposetruefalse или предикат, вернувший false, передаёт dispose вашему коду.
onDisposeErrorприёмник адаптераПринимает ошибки очистки scope запроса: Fastify request.log.error, Koa ctx.app.emit('error'), остальные console.error.
skipInferdiDispose(...)-Помечает один запрос как принадлежащий приложению для стриминга или фоновой работы.

Правила ошибок и владения

  • Ошибка setup поднимает только исходную ошибку. Если setupScope бросает, адаптер очищает полусобранный scope и пробрасывает именно эту ошибку. Сбой teardown во время такой очистки идёт в onDisposeError или приёмник ошибок и никогда не добавляется к проброшенной ошибке.
  • Упавший запрос всё равно очищается. skipInferdiDispose подавляет очистку только при успешном ответе; путь с ошибкой очищает scope независимо от маркера. Express - исключение: его callback middleware не видит обработанную ошибку маршрута, поэтому упавший Express-запрос с пропущенной автоочисткой остаётся во владении приложения.
  • autoDispose: false и skipInferdiDispose передают владение. Тогда ваш код сам очищает scope в правильной точке жизненного цикла фреймворка.
  • Ошибки очистки после отправленного ответа уходят в приёмник ошибок и проглатываются. Ответ уже отправлен, поэтому поздний сбой teardown не может его испортить.

Важные различия

АдаптерРазница
FastifyОчищает scope в onResponse; очистка при abort идёт через onRequestAbort; очистка root включается через disposeRootOnClose.
HonoОчищает scope после await next(); streaming helpers могут вернуть ответ до завершения работы stream, поэтому часто нужен skipInferdiDispose.
KoaЖдёт события Node response finish или close, поэтому обычные тела потоковых ответов не требуют skip.
ExpressНе может увидеть обработанную downstream-ошибку маршрута из callback middleware; упавший запрос с пропущенной автоочисткой остаётся во владении приложения.
ElysiaОчистка привязана к onAfterResponse; если hook не достигается, адаптер не может освободить ресурсы внутри scope.