Configuration

This page describes how the Vite-built SPA picks up its backend address and other deploy-time settings without requiring a rebuild.

API base URL resolution

Axios resolves the backend base URL via resolveApiUrl() in src/lib/api-url.ts:

  1. Call getRuntimeConfig() (see below) and return API_URL from the parsed object.

  2. If getRuntimeConfig() throws (runtime config missing or invalid), fall back to the relative path /configs-service. The fallback is useful for tests, SSR snapshots, and the Vite dev server (which proxies /configs-service to http://localhost:8082).

No other sources are consulted. There are no NEXT_PUBLIC_* or CONFIGS_API_URL environment variables read by the browser — those are artifacts of the previous Next.js stack.

Runtime config injection

The SPA reads a single global object:

window.__RUNTIME_CONFIG__ = { API_URL: "/configs-service" };

This object is populated by /runtime-config.js, which index.html loads synchronously in <head> before the app bundle:

<!-- index.html (excerpt) -->
<head>
  <script src="/runtime-config.js"></script>  <!-- writes window.__RUNTIME_CONFIG__ -->
  <!-- ... -->
  <script type="module" src="/src/main.tsx"></script>
</head>

Because the tag is synchronous and placed before the module script, window.RUNTIME_CONFIG is already set by the time React boots and the first import of src/lib/axios.ts resolves the base URL.

Development vs production

  • Development: public/runtime-config.js is a checked-in stub with safe defaults. Vite serves it verbatim.

  • Production: the file at /usr/share/nginx/html/runtime-config.js is regenerated at every container start by docker/ops/entrypoint.sh before nginx starts:

    # docker/ops/entrypoint.sh
    echo 'window.__RUNTIME_CONFIG__ = {"API_URL":"/configs-service"};' > "$WWW/runtime-config.js"

    The API_URL path is fixed to /configs-service — it must match both the nginx proxy location and the Spring Boot context-path of ft-configs-service. The per-deployment backend host/port is configured separately via the BACKEND_URL env var (the nginx proxy_pass target — see below), so the same image is redeployable across environments with only env var changes.

Zod schema validation (fail-fast)

src/lib/runtime-config.ts validates the runtime config with Zod before any downstream code sees it:

// src/lib/runtime-config.ts
import {z} from 'zod';

const RuntimeConfigSchema = z.object({
  API_URL: z.string().min(1),
});

export type RuntimeConfig = z.infer<typeof RuntimeConfigSchema>;

export function getRuntimeConfig(): RuntimeConfig {
  const raw = (window as any).__RUNTIME_CONFIG__;
  if (!raw) {
    throw new Error('Runtime configuration not loaded: window.__RUNTIME_CONFIG__ is undefined');
  }
  const parsed = RuntimeConfigSchema.safeParse(raw);
  if (!parsed.success) {
    throw new Error('Invalid runtime configuration: ' + parsed.error.message);
  }
  return parsed.data;
}

Behavior:

  • If /runtime-config.js failed to load or did not execute, getRuntimeConfig() throws "Runtime configuration not loaded…​". resolveApiUrl() catches this and falls back to /configs-service.

  • If the object exists but does not match the schema (e.g., API_URL missing or empty), it throws "Invalid runtime configuration: …​" with the Zod error details. This is intentional fail-fast behavior — invalid config must not silently degrade to the fallback in production, so call sites that care (health checks, diagnostics) can surface the error.

Build-time environment variables (Vite)

Values that are safe to bake into the bundle are exposed via import.meta.env.VITE_*. Vite inlines them at build time and strips all other process.env references.

| Variable | Source | Purpose | VITE_APP_VERSION | version.json via define in vite.config.ts | Displayed on the home page and in debug output. | VITE_APP_BUILD | version.json via define in vite.config.ts | CI build number, same surfaces. | import.meta.env.MODE | Vite built-in (development / production) | Gate debug-only code paths.

Do not put backend URLs or secrets into VITE_* — they become part of the static bundle and cannot be changed without a rebuild. Use window.RUNTIME_CONFIG for per-deployment values instead.

Runtime environment variables (container)

Consumed by docker/ops/entrypoint.sh and the nginx templates, never by the browser:

| Variable | Default | Purpose | BACKEND_URL | http://ft-configs-service:8080 | proxy_pass target for /configs-service/ and /ready. Must be scheme://host[:port] with no path — a path component makes nginx replace the entire client URI and breaks routing. | METRICS_ALLOW_IP | 0.0.0.0/0 | Extra CIDR/IP allowed to scrape /nginx_status, on top of the built-in 127.0.0.1, 10.0.0.0/8, 172.16.0.0/12. The default allows all clients; restrict it to the scraper’s CIDR in production.

Locales

locales/en.json and locales/ru.json are statically imported from src/contexts/i18n-provider.tsx — they are bundled by Vite and served as regular JS chunks. Additional copies are also present under public/locales/ and served as static files by nginx for tooling that prefers to fetch them over HTTP. There is no API route or rewrite involved; locale selection happens entirely on the client (cookie → navigator.languageen default).