Skip to content

Example: the Angular SPA on Vercel

The generic recipe is in Onboard an app; this page shows it filled in for an off-cluster frontend — the Penvoice web SPA on Vercel — wired back to backends running on Ultron Infra.

The web app (penvoice-web, Angular 21) is the one piece of this example not on the cluster — it deploys to Vercel. It runs two environments, and the git branch selects the backend: main builds prod, any other branch (e.g. staging) builds the staging config that points at the in-cluster test backends and serves at test.penvoice.app.

flowchart LR
  Main([branch: main]) -->|VERCEL_ENV=production| Prod[ng build --configuration production]
  Stg([branch: staging]) -->|VERCEL_ENV != production| Sg[ng build --configuration staging]
  Prod --> EP[environment.prod.ts] --> BP[api.penvoice.co.zw + auth.webbies.dev]
  Sg --> ES[environment.staging.ts] --> BS[test-api.penvoice.app + test-auth.webbies.dev]
  Local([ng serve :6006]) --> ED[environment.ts] --> BL[localhost:8080]

Vercel runs vercel-build, which branches on VERCEL_ENV — Vercel sets this to production only for the production branch (main), and preview for every other branch:

"vercel-build": "if [ \"$VERCEL_ENV\" = production ]; then ng build --configuration production; else ng build --configuration staging; fi"

So main--configuration production; staging (and any preview) → --configuration staging.

angular.json swaps the environment file per configuration via fileReplacements. The staging config replaces environment.ts with environment.staging.ts:

"staging": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.staging.ts"
}
],
"outputHashing": "all"
}

production does the same with environment.prod.ts and is the defaultConfiguration.

ConfigFileOIDC issuerapiBaseUrlredirectUri
dev (ng serve)environment.tstest-auth.webbies.dev/realms/penvoicehttp://localhost:8080http://localhost:6006/
stagingenvironment.staging.tstest-auth.webbies.dev/realms/penvoicehttps://test-api.penvoice.apphttps://test.penvoice.app/
productionenvironment.prod.tsauth.webbies.dev/realms/penvoicehttps://api.penvoice.co.zwhttps://app.penvoice.co.zw/

All three use OIDC client penvoice-web. Staging targets the in-cluster test Keycloak and API documented in the other examples; production targets the future prod instances.

// environment.staging.ts — used by the `staging` branch → test.penvoice.app
export const environment = {
production: true,
oidc: {
issuer: 'https://test-auth.webbies.dev/realms/penvoice',
clientId: 'penvoice-web',
redirectUri: 'https://test.penvoice.app/',
postLogoutRedirectUri: 'https://test.penvoice.app/',
},
apiBaseUrl: 'https://test-api.penvoice.app',
};

ng serve runs on port 6006 (not Angular’s default 4200) and uses the unreplaced environment.ts: OIDC still points at the in-cluster test Keycloak (test-auth.webbies.dev), but apiBaseUrl is http://localhost:8080 — so a local SPA talks to a locally-run API while authenticating against the real test Keycloak.