feat(spa): proxy /api in dev-server, relative bffApiBaseUrl #259
Reference in New Issue
Block a user
Delete Branch "feat/spa-dev-proxy"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Make the SPAs reach the BFF as a same-origin call via an Angular dev-server
/apiproxy. Solves the "Backend unreachable" error surfaced during the ADR-0030 dockerised dev mode VM validation when the SPA is accessed from a remote browser (e.g.http://<vm-ip>:4200/), and bypasses CORS in dev altogether. Follow-up to the just-merged ADR-0030 implementation.Root cause it fixes
Before this PR, both SPAs hardcoded
bffApiBaseUrl: 'http://localhost:3000/api'(per ADR-0018) and the BFF'sCORS_ALLOWED_ORIGINSonly allowedhttp://localhost:4200,http://localhost:4300. Both assumptions hold for nativenx serve(developer on the same machine as the BFF) but break the moment the browser sits on a different host than the BFF — exactly the case for the ADR-0030appsCompose profile: openhttp://<vm-ip>:4200/from your workstation and the SPA'slocalhost:3000call hits your workstation's loopback (nothing there), not the VM's BFF. Even if the URL were right, the originhttp://<vm-ip>:4200is not in the CORS allowlist.Fix
Switch to a same-origin dev pattern: the Angular dev-server proxies
/api/*to the BFF, andenvironment.tsuses a relative'/api'URL.apps/portal-shell/proxy.conf.js/api → ${BFF_TARGET:-http://localhost:3000}(JS form so the env var can swap the target at startup).apps/portal-admin/proxy.conf.jsapps/portal-shell/project.json,apps/portal-admin/project.jsonserve.options.proxyConfigpoints at the new file.apps/portal-shell/src/environments/environment.ts,apps/portal-admin/src/environments/environment.tsbffApiBaseUrl: 'http://localhost:3000/api'→'/api'. The comment block explains the rationale + how production siblings can still use an absolute origin if SPA + BFF live on different hosts.apps/portal-shell/src/observability/tracing.tsnew URL(environment.bffApiBaseUrl)would throw on a relative URL — resolved againstwindow.location.originso both relative (dev) and absolute (prod cross-origin) bases work.infra/local/dev.compose.ymlBFF_TARGET=http://portal-bff:3000added toportal-shellandportal-adminso the proxy hits the BFF container by name (Compose DNS) when theappsprofile is up. Nativenx serveleaves the var unset and falls back tolocalhost:3000.Why a relative URL is safe
${bffApiBaseUrl}/healthetc. compose to/api/health— relative paths work infetch/HttpClient.tracing.tspropagatestraceparenton requests whose origin matches the BFF origin. Resolving the relative base againstwindow.location.origingives the current page's origin, which is exactly the origin the dev-server proxy serves from — so the regex still matches the right requests in dev. In a future cross-origin production deployment,environment.prod.tscan set an absolutebffApiBaseUrl; the URL constructor's second arg is ignored when the first is absolute, so the same code path keeps working.feature-auth/auth.config.ts): consumesbffApiBaseUrlvia DI as a string prefix — agnostic to absolute vs relative.Scope notes
environment.otlpEndpoint = 'http://localhost:4318/v1/traces') and the cross-SPA links (adminAppUrl,shellAppUrl) remain absolute. They hit the same remote-browser problem onapps-profile access, but neither is blocking the user-visible "Backend unreachable" path this PR targets. Out of scope here; a follow-up could either proxy them too or surface them via runtime config.bffApiBaseUrloverrides remain the supported lever (ADR-0018), unchanged.Test plan
docker compose -f dev.compose.yml --profile apps configstill validates../infra/local/dev.sh up apps, then in the workstation browser openhttp://<vm-ip>:4200/→ SPA loads, the "Backend unreachable" message is gone, network tab shows/api/...calls succeeding (same-origin, no CORS preflight).nx serve portal-shellstill works (the proxy falls back tolocalhost:3000).traceparent) appear on/api/*fetches in the browser network tab.pnpm exec nx affected -t test buildgreen on the two SPAs.Related
environment.tsper-env strategy this complements (does not supersede — production behaviour unchanged).Make the SPAs reach the BFF same-origin via the Angular dev-server's proxy, so the dockerised dev mode (ADR-0030) works when the SPA is accessed from a remote browser (e.g. http://<vm-ip>:4200/) — and CORS is bypassed in dev altogether. - New proxy.conf.js per SPA: /api -> ${BFF_TARGET:-http://localhost:3000} (JS form so the env var can swap the target at startup without a rebuild). - project.json serve.options.proxyConfig wired in for both apps. - environment.ts (shell + admin) bffApiBaseUrl: 'http://localhost:3000/api' -> '/api'. Production siblings can still set an absolute origin if the SPA and BFF live on different hosts; tracing.ts resolves either form against window.location.origin. - tracing.ts: new URL(env, window.location.origin) — relative bases no longer throw, absolute bases keep their own origin. - dev.compose.yml: BFF_TARGET=http://portal-bff:3000 on portal-shell and portal-admin so the proxy hits the BFF container by Compose DNS. Native nx serve leaves it unset and falls back to localhost. OTel exporter URL and cross-SPA links remain absolute — same remote- browser issue, but not blocking the 'Backend unreachable' path this PR targets. Out of scope here, follow-up if needed. Stacked on top of feat/dockerised-dev-mode.