FixPortalDocsSimulator
Browse documentation

REST contract & codegen workflow

The backend owns the wire contract. The frontend consumes committed snapshots, generates REST type declarations, then exposes hand-maintained application types to the rest of the UI.

Files

  • contracts/rest/simulator.openapi.json is the OpenAPI input.
  • src/api/generated/ contains generated declarations only; it is not an HTTP client and must not be edited.
  • src/api/types.ts is the application facade.
  • contracts/signalr/simulator.signalr.json describes live events.
  • contracts/typescript/simulator.types.snapshot.json records selected application-facade shapes for backend parity tests.

Change sequence

Change DTO, update testsbackend repoPublish snapshotsOpenAPI + SignalR + TS shapesCopy contracts, generate typesnpm run generate:rest-typesAdapt facade + callerssrc/api/types.tsRun contract tests + gatesparity + signalrContract
The backend changes its DTO and publishes contract snapshots first; the frontend copies them, regenerates types, adapts the facade, then runs both repositories' contract tests before landing.
  1. Change the backend DTO or endpoint, update its tests, regenerate its published snapshots, and land that backend change first.
  2. Copy the REST and SignalR snapshots into the frontend's contracts/ directory.
  3. Run npm run generate:rest-types.
  4. Adapt src/api/types.ts, src/api/signalrEvents.ts, and callers as needed.
  5. Run npm run snapshot-types after changing a supported facade type.
  6. Validate the generated TypeScript snapshot at the backend parity seam.
  7. Run the frontend contract tests and both repositories' gates.
  8. Land the compatible frontend change.

Generated names are not the product API. Keep generated imports inside the facade and the few explicit contract tests. This limits generator churn and gives UI code one stable vocabulary.

Compatibility rules

Adding an optional response field is usually additive. Removing, renaming, changing requiredness, narrowing an enum, or changing nullability is breaking until every consumer moves. Property names are camelCase on the wire and must agree across DTOs, OpenAPI, generated types, and the snapshot.

SignalR events need the same discipline. src/api/signalrContract.test.ts compares handler payload types with the SignalR snapshot, so a REST-green change does not prove live payload compatibility.

Diagnose drift

If generation changes unexpectedly, diff the OpenAPI snapshot first. If backend parity fails, regenerate the frontend snapshot from the compatible facade rather than editing JSON by hand. If UI code imports generated modules directly, move that use behind src/api/types.ts before resolving the shape.

Never invent the frontend DTO first. The backend is authoritative, and its parity suite is designed to reject a frontend-only contract.

Updated 2026-09-10 · Edit on GitHub