Skip to content

UIGen API Explorer

UIGen auto-generates a React UI from the backend's OpenAPI spec. It gives you tables, forms, pagination, and a built-in API proxy — so you can browse and interact with every endpoint without writing any frontend code.

UIGen is a deployed service accessible at its own subdomain in every environment. The Admin button in the portfolio nav opens it.

Environment URL Auth
Production admin.sven-relijveld.com Public — guest read-only, admin token for writes
Dev admin.dev.sven-relijveld.com AAD login required (EasyAuth)
Local localhost:4400 Same guest/admin token flow

Auth model

UIGen uses two parallel auth paths depending on the environment:

Guest / admin token (all environments)

  • Guest — issued automatically with no credentials. Grants read-only access to all GET endpoints plus contact and CV download.
  • Admin — issued by presenting the API_ADMIN_TOKEN bearer token to GET /api/uigen-token. Grants full write access to all endpoints.

The Admin button in the portfolio nav opens a modal where you can continue as a guest or enter the admin token. The backend issues a scoped token which is then written to sessionStorage on the UIGen origin via the shim proxy's /auth endpoint.

OAuth PKCE flow (UIGen native login)

UIGen also supports a native OAuth PKCE login flow against Entra ID. This is the primary path for the feature-flags override panel:

  1. UIGen redirects to Entra ID authorize endpoint
  2. User authenticates and consents
  3. Entra redirects back to /auth/callback with an authorization code
  4. UIGen exchanges the code for an access token
  5. UIGen calls GET /auth/me (the backend's session validation endpoint) with the access token as a Bearer token
  6. Backend validates the token via PyJWT + JWKS and returns {id, email, name, role}
  7. UIGen stores the token in sessionStorage and considers the session authenticated

The /auth/token and /auth/callback paths are excluded from UIGen's EasyAuth gate on dev so the PKCE exchange can complete. The backend registers the auth router at both /api/auth and /auth so UIGen's proxy can reach /auth/me without the /api prefix.

In dev, EasyAuth still protects the UIGen root — AAD login is required to reach UIGen at all. Only the specific OAuth callback paths bypass EasyAuth.


Starting UIGen locally

bash
# Start just the services UIGen needs
docker-compose up postgres backend uigen

# Or start the full stack
docker-compose up

UIGen is available at http://localhost:4400 once the backend healthcheck passes (~15–30 seconds on first run while the image builds).


How it works

Browser → http://localhost:4400 (shim proxy)
              ├─ GET /auth?token=<t>
              │    writes sessionStorage["uigen_auth"]
              │    redirects to /
              └─ everything else → UIGen on :4401
                                        ├─ Serves React SPA (generated from OpenAPI)
                                        └─ Built-in proxy: /api/* → BACKEND_URL
  1. The portfolio nav opens <VITE_UIGEN_URL>/auth?token=<scoped-token>
  2. The shim proxy writes the token to sessionStorage["uigen_auth"] on the UIGen origin and redirects to /
  3. UIGen reads the token and sends it as Authorization: Bearer <token> on every API call
  4. UIGen's built-in proxy forwards calls to the backend, stripping the /api prefix

The shim also injects portfolio pink CSS (--primary: #ff33cc) into all HTML responses so UIGen matches the portfolio's theme.


Detected resources

After startup you will see all backend resources in the sidebar. UIGen derives resource groups from the x-uigen-id annotations on each endpoint in the OpenAPI spec.

Resource Key endpoints Access
Projects GET /api/projects, POST /api/projects, PUT/PATCH/DELETE /api/projects/{slug} Read: guest; Write: admin
Experience GET /api/experience, POST /api/experience, PUT/PATCH/DELETE /api/experience/{id} Read: guest; Write: admin
Skills GET /api/skills, POST/PUT/PATCH/DELETE /api/skills/categories/{id} Read: guest; Write: admin
Awards GET /api/awards Guest
Certifications GET /api/certifications Guest
Contact POST /api/contact Guest
Download CV GET /api/download-cv, PATCH /api/download-cv Guest
Feature Flags GET /api/feature-flags, PATCH /api/feature-flags Read: guest; Write: admin
CV Status GET /api/cv/status Guest
CV Generate PATCH /api/cv/generate Guest (feature-flagged)
Health GET /api/health Guest

Feature Flags Override Panel

UIGen includes a built-in feature-flags management panel implemented in .uigen/src/feature-flags.tsx. It is injected into the UIGen shim as an additional React component.

The panel:

  • Reads current flag state from GET /api/feature-flags on load
  • Renders toggles for each flag (cvGeneration, portfolioContentApi)
  • Writes changes via PATCH /api/feature-flags (requires admin auth)
  • The backend TTL cache is explicitly busted after a successful write so the next GET reflects the new value immediately

The panel is available at the UIGen admin portal and requires the user to be authenticated as admin. On dev, this means completing the EasyAuth + OAuth PKCE flow. On prod, it requires a valid Entra token from the portfolio-admins group.

docker-compose — the backend service receives FEATURE_* env vars for local flag overrides:

yaml
environment:
  - FEATURE_CV_GENERATION=${FEATURE_CV_GENERATION:-false}
  - FEATURE_PORTFOLIO_CONTENT_API=${FEATURE_PORTFOLIO_CONTENT_API:-false}

Frontend nav button

The Admin button in the top navigation bar opens the Admin modal. It is only rendered when VITE_UIGEN_URL is set to a non-empty value (baked in at build time).

Context VITE_UIGEN_URL value Button visible
Local docker-compose http://localhost:4400 ✅ Yes
Prod build (CI) https://admin.sven-relijveld.com ✅ Yes
Dev build (CI) https://admin.dev.sven-relijveld.com ✅ Yes
Build without env var `` (empty) ❌ No

docker-compose service

UIGen is built from Dockerfile.uigen which installs the UIGen packages at image build time:

yaml
uigen:
  build:
    context: .
    dockerfile: Dockerfile.uigen
  container_name: portfolio-uigen
  ports:
    - "127.0.0.1:4400:4400"
  depends_on:
    backend:
      condition: service_healthy
  networks:
    - portfolio-network
  environment:
    - BACKEND_URL=http://portfolio-backend:8000
  restart: on-failure

Key design decisions:

  • Dockerfile.uigen: packages are installed at build time (not runtime), so the container starts immediately with no install step
  • 127.0.0.1 binding: port is only accessible on localhost, not all network interfaces
  • BACKEND_URL env var: the container fetches the OpenAPI spec from this URL at startup and patches it so UIGen's proxy targets the backend correctly

Azure deployment

UIGen runs as a Container App (ca-ui) alongside the frontend and backend:

  • Image: portfolio-uigen pushed to ACR, built from Dockerfile.uigen
  • Managed Identity: own user-assigned identity with AcrPull + (on dev) Key Vault Secrets User
  • Custom domain: admin.sven-relijveld.com (prod) / admin.dev.sven-relijveld.com (dev) with managed TLS cert
  • Scale: 0–1 replicas (scale-to-zero; UIGen is on-demand)
  • Dev auth: EasyAuth with unauthenticatedClientAction: RedirectToLoginPage
  • BACKEND_URL: set to the backend Container App FQDN at deploy time

Troubleshooting

Admin button not visibleVITE_UIGEN_URL was empty at build time. Rebuild with the env var set.

/auth redirect loop — UIGen probing /api/uigen-token in the OpenAPI spec can cause a 401 loop. The endpoint uses include_in_schema=False to hide it from UIGen. If this reappears, check the spec.

API calls return errors — check the backend is healthy:

bash
docker-compose logs backend
curl http://localhost:8000/health

CSS theme not applied — the shim proxy must be running and buffering HTML. Check shim logs:

bash
docker-compose logs uigen

UIGen login loop / never reaches dashboard — a chain of issues was fixed across PRs #443–#451. If you see a redirect loop after Entra login:

  1. Verify sessionValidationEndpoint in .uigen/config.yaml is set to /auth/me (no /api prefix — the shim proxy handles it)
  2. Verify the shim injects the Bearer token from sessionStorage into /api/auth/me requests
  3. Verify the Entra app registration has requestedAccessTokenVersion: 2 in the manifest
  4. Verify the localhost redirect URI is on the SPA platform (not Web) in the Entra app registration

Feature flags panel shows no flags / PATCH fails 403 — the backend MI needs App Configuration Data Owner (not just Data Reader) on the App Configuration store. Check RBAC assignments.


See Also

Page history

Field Value
Last updated 2026-05-19

Changelog

Date PRs Summary
2026-04-18 #205 Initial UIGen API Explorer page
2026-04-18 #207 UIGen promoted to deployed service; admin.sven-relijveld.com; auth model; Dockerfile; updated flow
2026-05-03 #358, #362 Update detected resources table to reflect removal of /admin/ prefix, addition of awards/certifications, PATCH cv/generate, and portfolioContentApi flag Stale resource table listing /admin/* groupings
2026-05-19 #438, #439, #443, #444, #446, #447, #448, #451, #452, #455 Add Feature Flags Override Panel section; update Auth model to document OAuth PKCE flow and /auth/me endpoint; add UIGen OAuth login loop fix notes to troubleshooting; update EasyAuth note