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

1. Change DTO, tests, snapshotsbackend repo, land first2. Copy snapshots into contracts/REST and SignalR3. Generate typesnpm run generate:rest-types4. Adapt facade and callerssrc/api/types.ts, signalrEvents.ts5. Refresh the TS snapshotnpm run snapshot-types6. Validate at the parity seambackend parity tests7. Run contract tests and gatesboth repositories8. Land the frontend changecompatible with the landed backend
All eight steps of the change sequence: the backend lands its DTO change and publishes snapshots, then the frontend copies them, regenerates types, adapts the facade, refreshes the parity snapshot, validates it, runs contract tests, and lands the compatible change.
  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-25 · Edit on GitHub