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-typesrunsscripts/generate-api-types.mjs, which fetches the backend's committedopenapi.json— pinned to a specifichlmiscommit (HLMIS_COMMITin the script), not a moving branch — and writessrc/core/api/generated/schema.d.tsviaopenapi-typescript.- The backend repo is private, so the script needs a GitHub token
(
GITHUB_TOKEN, orgh auth token). - It also writes
docs/api-manual-check.mdlisting 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'sReadControllerhas no@ApiResponsedecorators, so these serialise asRecord<string, never>in the spec and there is nothing to alias onto. Documented, not yet fixed on the backend. - The DHIS2-only
organizationUnitsDTOs — 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.