ADR-UI-014. Рантайм-конфигурация

ADR Версия: 1.0 accepted

Фронт должен знать адрес API (и другие параметры среды) в момент старта. Параметры различаются по средам (local/dev/stage/prod), но сборка одна. Фиксируем: параметры грузятся из config.json в рантайме (не build-time environment.ts) через provideAppInitializer до bootstrap; apiBaseUrl берётся только из RuntimeConfigService; деплой подменяет только config.json.

⟨/⟩ Исходник

1. Контекст и постановка задачи

Приложению нужен адрес API (и потенциально другие параметры) на старте. Эти параметры различаются между средами (локальная разработка, dev-стенд, stage, прод), но артефакт сборки должен быть один — иначе выпуск в каждую среду требует отдельного билда, что усложняет релиз и повышает риск расхождений.

Стандартный шаблон Angular (environments/environment.ts) не подходит: он работает на этапе сборки, а не старта. Нужен способ подменять конфиг у уже собранного приложения.

У нас фронт и API на стенде — на разных origin, поэтому apiBaseUrl — абсолютный, а бэк держит CORS-allowlist по среде (см. связанный backend-ADR).

2. Драйверы решения

  1. Один билд на все среды — конфиг подменяется без пересборки.
  2. Конфиг гарантированно готов к первому запросу — никакой код не обратится к API раньше, чем конфиг загружен.
  3. Прозрачный доступapiBaseUrl берётся инъекцией очевидного сервиса, без чтения window и глобальных объектов.
  4. Стандартный механизм Angular — без хаков в index.html.

3. Рассмотренные варианты

4. Решение

Выбран Вариант A: config.json грузится через provideAppInitializer и кладётся в RuntimeConfigService.

4.1. Контракт config.json

Файл — в src-ui/public/config.json (Angular кладёт содержимое public/ в корень dist). Это рантайм-артефакт: на каждой среде заменяется своей версией средствами деплоя.

{ "apiBaseUrl": "https://sok-api.dev.example" }
Ключ Тип Назначение
apiBaseUrl string Origin API — без завершающего слэша и без /api.

Расширение состава ключей — дополнением этого раздела, без нового ADR.

4.2. RuntimeConfigService

provideAppInitializer(() => inject(RuntimeConfigService).load()),

4.3. Связка с генерированным API-клиентом

Базовый URL сгенерированного клиента (@api/icd) задаётся токеном BASE_PATH — через фабрику из RuntimeConfigService (ADR-UI-012):

{ provide: BASE_PATH, useFactory: () => inject(RuntimeConfigService).apiBaseUrl },

Токен резолвится при первом создании api-сервиса (открытие страницы) — уже после инициализатора, конфиг гарантированно загружен.

4.4. Правила

  1. apiBaseUrl — только из RuntimeConfigService. Никаких window.*, прямого чтения config.json, импорта из environment.ts.
  2. config.json не кэшируетсяno-store в fetch и на отдаче с сервера; иначе смена конфига потребует жёсткого обновления у каждого пользователя.
  3. В репозитории лежит config.json с локальными значениями (http://localhost:5080); реальные значения сред — в артефактах деплоя (генерятся devops/configs-generator).
  4. Секретов в config.json нет — значения видны в network tab; только публичные параметры.

5. Положительные следствия

6. Отрицательные следствия и компромиссы

7. Проверка

8. Открытые вопросы / отложено

Связанные артефакты

Документы