0002. OpenAPI 3.0.3 contract with generated Go and TypeScript code
- Status: Accepted
- Date: 2026-09-30
Context
The backend (Go) and frontend (TypeScript) are built by different agents at the same time. Both sides need the same request, response and WebSocket message types. Types written by hand on each side drift apart, and a mismatch in a price or size field can place a wrong order.Decision
api/openapi.yamlis the single source of truth for REST endpoints and WebSocket messages. WebSocket messages are defined as JSON Schema components in the same file.- The spec uses OpenAPI 3.0.3, not 3.1, because
oapi-codegenonly partly supports 3.1. task gengenerates:- the Go strict server and types with
oapi-codegenintobackend/internal/httpapi/gen; - the TypeScript types with
openapi-typescript, used throughopenapi-fetch, intofrontend/src/lib/api/gen.
- the Go strict server and types with
- Generated code is never edited by hand. To change a type, edit the spec and run
task gen. - Contract changes land in their own PR before the code that uses them. Each phase starts with the contract diff.
- Wire rules: snake_case field names, RFC 3339 UTC timestamps, money, prices and sizes
as decimal strings, never a bare
modefield.
Consequences
- Frontend and backend work in parallel against the same generated types from the start of a phase.
- A contract mismatch becomes a compile error on one side instead of a runtime bug.
- 3.1-only features are not available. Nullable fields use
nullable: true, and JSON Schema keywords added in 3.1 cannot be used. - The codegen tool versions must be pinned so that every machine generates the same code.
- Contract changes go through the tech lead, which adds a step to changes that touch both sides.
Alternatives considered
- OpenAPI 3.1. Aligns with current JSON Schema, but
oapi-codegensupport is incomplete, so generated Go code would be unreliable. - Hand-written types on each side. No tooling, but the two sides drift.
- gRPC or Connect with protobuf. Strong typing, but the browser needs an extra transport layer, and payloads are harder to inspect in browser dev tools.
- Generate TypeScript from Go types. Makes Go the source of truth and leaves no reviewable contract file for parallel work.