Skip to main content

API Types & Versioning

Generated schema, hand-written aliases

The frontend's API types started as a ~1,000-line hand-maintained src/core/api/types/index.ts. They are being moved onto a generated schema instead:

  • pnpm generate:api-types runs scripts/generate-api-types.mjs, which fetches the backend's committed openapi.jsonpinned to a specific hlmis commit (HLMIS_COMMIT in the script), not a moving branch — and writes src/core/api/generated/schema.d.ts via openapi-typescript.
  • The backend repo is private, so the script needs a GitHub token (GITHUB_TOKEN, or gh auth token).
  • It also writes docs/api-manual-check.md listing every endpoint the spec documents with no response body — those can't be type-generated and need a human to check the real shape.

Hand-written types now mostly Pick<> / alias into the generated schema rather than restating fields. What stays genuinely hand-written:

  • Envelope wrappers ({ payload }, PaginatedResponse<T>) — not in the spec.
  • Import/preview result shapes, runtime-config, generic-report metadata.
  • The legacy /read/View* endpoints (view-mode.ts) — the backend's ReadController has no @ApiResponse decorators, so these serialise as Record<string, never> in the spec and there is nothing to alias onto. Documented, not yet fixed on the backend.
  • The DHIS2-only organizationUnits DTOs — correctly absent from the deployment-agnostic spec, so kept as local interfaces.

When you touch an endpoint's payload: re-run pnpm generate:api-types (bumping HLMIS_COMMIT if the backend change is merged) and swap the relevant hand-written type to alias the regenerated schema.

Runtime version tolerance

hlmis-frontend and hlmis are separate repos on separate deploy cadences, so a running frontend can meet a backend it wasn't built against. Two mechanisms cover it:

The contract-version check

src/core/lib/version-compat.ts holds MIN_API_CONTRACT_VERSION / MAX_API_CONTRACT_VERSION (both 1 today). compareApiContract(backendVersion) returns compatible / backend-too-old / backend-too-new. <VersionMismatchBanner> (mounted in the dashboard layout) reads apiContractVersion off /config and shows a dismissible banner when they diverge — backend-too-old warns features may break, backend-too-new suggests updating the frontend. Dismissal is per-status in localStorage.

Bump MAX_API_CONTRACT_VERSION when the frontend learns to speak a new backend contract; bump MIN only when it drops support for an old one.

Feature-flag / rollout gating

New backend endpoints that aren't live everywhere yet are gated through the existing /config capabilities plumbing rather than shipped assuming the endpoint exists — the same mechanism deployment-mode toggles use, reused for rollout.