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:
-
Call
getRuntimeConfig()(see below) and returnAPI_URLfrom the parsed object. -
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-servicetohttp://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.jsis a checked-in stub with safe defaults. Vite serves it verbatim. -
Production: the file at
/usr/share/nginx/html/runtime-config.jsis regenerated at every container start bydocker/ops/entrypoint.shbefore nginx starts:# docker/ops/entrypoint.sh echo 'window.__RUNTIME_CONFIG__ = {"API_URL":"/configs-service"};' > "$WWW/runtime-config.js"The
API_URLpath is fixed to/configs-service— it must match both the nginx proxylocationand the Spring Bootcontext-pathof ft-configs-service. The per-deployment backend host/port is configured separately via theBACKEND_URLenv var (the nginxproxy_passtarget — 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.jsfailed 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_URLmissing 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.
See Installation & Deployment — Environment Configuration and TLS Certificate for the full container story.
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.language → en default).