Skip to content

ADR-0005: Publish OpenAPI + Generated TypeScript Types as the Platform Contract

Status: Proposed Date: 2026-05-03 Deciders: Architecture team Impacted Repos: reservation-api-server, all frontend repos

Context

Frontends consume Frappe's REST API but have no compile-time guarantee that the API surface they depend on actually exists. Today:

  • Field renames in BZBooking are discovered in production.
  • bookingzone.api.payment.process_payment argument changes break admin-web-app only after deploy.
  • Each frontend hand-writes types for API responses, drifting from reality.

The cost of this drift is real: incidents, hot-fixes, and erosion of agent trust in the codebase.

Decision

The backend publishes machine-readable contracts; frontends consume them as a versioned dependency.

Pipeline

reservation-api-server
  └── CI: extract-openapi.py
        ├── walks @frappe.whitelist() methods
        ├── reads @standard_api decorator metadata
        ├── reads BZ* doctype JSON for entity schemas
        └── emits openapi.json + doctype-schemas.json
                ↓
                ↓ committed/published to bz-platform-context
                ↓
bz-platform-context/contracts/
  ├── openapi.json            ← canonical API surface
  ├── doctype-schemas.json    ← canonical entity field lists
  └── generated-types/
        └── @bookingzone-types/  (npm package, semver versioned)
                ↓
                ↓ pnpm install @bookingzone/api-types@x.y.z
                ↓
admin-web-app, venuplus-app, customer-web-app
  └── compile-time type errors when backend breaks contract

Versioning rules

  • Backend bumps patch when adding non-breaking endpoints/fields.
  • Backend bumps minor when deprecating fields (with @deprecated doc).
  • Backend bumps major for breaking changes.
  • Frontend repos pin a major version; CI auto-PRs minor/patch bumps.
  • Major version bumps require coordinated rollout + cross-cutting spec.

Naming

  • npm package: @bookingzone/api-types
  • Type exports follow doctype naming: BZBooking, BZOutlet, etc.
  • Endpoint exports: paths['/api/method/...']-style (openapi-typescript convention).

Consequences

Positive

  • Drift caught at frontend build time, not runtime.
  • AI agents in any repo can answer "does field X exist on BZBooking?" by reading the contract instead of guessing.
  • Onboarding: new frontend engineer gets autocomplete on the entire backend.

Negative / Trade-offs

  • Backend CI gains a new responsibility (contract extraction + publish).
  • Frontends must adopt the package and stop hand-typing API responses.
  • Initial extraction script needs careful coverage of edge cases (Frappe's flexible response shapes).

Risks / Mitigations

  • Risk: extraction misses methods. Mitigation: lint rule that fails CI if a @frappe.whitelist() method is missing OpenAPI metadata.
  • Risk: generated types diverge from runtime response. Mitigation: contract tests in CI validate sample responses against the schema.

Alternatives Considered

  • Hand-written types per frontend: status quo; rejected (drift problem).
  • GraphQL gateway: rejected; introduces a major new component for marginal benefit over OpenAPI + types.
  • Frappe's built-in @frappe.whitelist introspection only (no schema): insufficient for compile-time checking.

Implementation Notes

This ADR is Proposed, not yet Accepted. Acceptance requires:

  1. Spike a working extract-openapi.py that handles 80% of methods.
  2. Confirm openapi-typescript produces usable types for BZBooking, BZOutlet, BZPaymentTransaction.
  3. Define the npm publish flow (private registry vs. GitHub Packages).
  4. Pilot with one frontend (admin-web-app) before mandating org-wide.

A separate cross-cutting spec (SYS-002-api-contract-pipeline) will track the implementation.

References

  • system-constitution.md Principle IV
  • ADR-0004 (Spec-Driven Development)