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
BZBookingare discovered in production. bookingzone.api.payment.process_paymentargument changes breakadmin-web-apponly 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
@deprecateddoc). - 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.whitelistintrospection only (no schema): insufficient for compile-time checking.
Implementation Notes¶
This ADR is Proposed, not yet Accepted. Acceptance requires:
- Spike a working
extract-openapi.pythat handles 80% of methods. - Confirm
openapi-typescriptproduces usable types forBZBooking,BZOutlet,BZPaymentTransaction. - Define the npm publish flow (private registry vs. GitHub Packages).
- 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.mdPrinciple IV- ADR-0004 (Spec-Driven Development)