Appearance
OpenAPI Contract Migration
Related docs: Frontend Integration Guide, Backend Contract, Implementation Tracks
Status
Implemented on the OpenAPI migration branch. Use this page as the rationale and migration record for replacing handwritten frontend/backend API contracts with an OpenAPI-first contract layer.
Decision
OpenAPI should become the source of truth for the client-facing HTTP contract: the routes the rewritten frontend can reach through its Backend-for-Frontend gateway.
The migration should happen in one focused implementation branch before production deployment. That branch should add the OpenAPI spec, generate TypeScript contracts from it, migrate frontend imports, remove the old handwritten shared API contract files, and update docs/scripts/tests together.
Keep shared/ only for generated HTTP contract types and truly shared cross-runtime primitives. Do not keep it as a broad runtime-sharing package or a place for backend-internal module contracts.
Because the backend is currently a monolith, there is no separate internal HTTP API to specify now. Backend modules should call each other in-process through module APIs, and those TypeScript contracts should live with the owning module.
Rationale
The goal is not standardization for its own sake. The goal is to make the frontend/backend boundary explicit, typed, validated, and hard to accidentally bypass.
The current product flows cross several backend owners: User/Auth, Clone Service, Economy, Lifecycle, AI Trading, and Backtesting. The frontend should consume those flows through a deliberate client API, not through backend module assumptions. The clone-creation path already showed why this matters: paid clone creation must go through Economy so SignedVault verification and entitlement grants happen before Clone Service records become user-visible.
OpenAPI is the right artifact for this boundary because it describes HTTP APIs in a standard, tool-readable way. Generated TypeScript contracts then make the frontend consume that boundary directly instead of relying on copied handwritten route/type files.
The same reasoning does not apply to in-process backend module calls. Those are monolith internals, so their contracts should stay with the owning module unless another runtime genuinely consumes them.
Current State
The rewritten frontend already has a Backend-for-Frontend gateway:
| Browser path | Frontend route | Backend target |
|---|---|---|
/api/backend/* | frontend/src/app/api/backend/[...path]/route.ts | /api/v1/* |
/api/auth/* | frontend auth route handlers | /api/v1/auth/* plus cookie/session handling |
The frontend should not call backend services or PUMP_PARTY_BACKEND_API_URL directly from browser code.
Before this migration, contract code was split across handwritten files in shared/src/*/api-contract.ts. The frontend copied these into frontend/src/lib/shared/ through copy:shared. That was useful during backend rewrite work, but it created three problems:
- Route/type contracts are not a standard API artifact.
shared/also contains legacy runtime code that the rewritten frontend should not depend on.- The frontend build depends on a copy step instead of generated contracts.
Target Shape
| Path | Purpose |
|---|---|
doc/openapi/client-api.openapi.yaml | Canonical OpenAPI 3.1 spec for client-facing HTTP routes. |
shared/src/generated/client-api.d.ts | Generated TypeScript types from the OpenAPI spec. |
shared/src/index.ts | Re-export generated contract types. |
frontend/src/lib/api/ | Frontend API client wrappers that call /api/backend/* and /api/auth/*. |
doc/client-api-contract.md | Human route-family reference backed by the OpenAPI contract. |
backend/src/api/* | HTTP route adapters typed against generated OpenAPI operations. |
backend/src/modules/*/contracts.ts | Optional module-local TypeScript contracts for in-process backend boundaries. |
Use client-api rather than public-api because most client-consumed routes are authenticated. Do not introduce internal-api or internal-modules until there is a real internal HTTP boundary or a proven need for a cross-module TypeScript package.
Remove these once the migration is complete:
frontend/src/lib/shared/- frontend
copy:shared/setup:sharedscripts - handwritten
shared/src/*/api-contract.tsfiles - legacy
shared/src/markets,shared/src/supabase, and other unused runtime helpers
Contract Ownership
| Contract area | Source of truth after migration |
|---|---|
| HTTP paths, methods, auth mode, request bodies, query params, and response shapes | OpenAPI |
| Generated TypeScript operation/schema types | shared/src/generated/client-api.d.ts |
Frontend fetch behavior, cookie/session refresh, and /api/backend/* routing | Frontend BFF route handlers and API wrappers |
| Backend HTTP request/response adapter types | Generated OpenAPI operation/schema types imported by backend/src/api/* only |
| Backend in-process function/module contracts | Owning backend module |
| Backend business rules and data persistence | Backend modules |
| Product vocabulary and lifecycle rules | Product/module docs |
OpenAPI describes the client HTTP boundary. It should not become the source of truth for runtime internals, worker behavior, prompt assembly, trading math, database schemas, or in-process module calls. Backend API route adapters may use generated OpenAPI types because they are the HTTP boundary; backend modules should remain independent of OpenAPI.
If an internal HTTP boundary is introduced later, it can get its own separate spec, such as doc/openapi/internal-http-api.openapi.yaml. Do not add internal monolith function signatures to OpenAPI.
Initial Scope
Cover frontend-facing, user-session routes first:
| Client surface | Include |
|---|---|
| Auth/session | SIWE challenge/verify, refresh, logout, current user. |
| Clones | List/read/update clones and entitlement read. |
| Workflow | Strategy draft/version/publish, backtest jobs, decision history, and workflow data previews. |
| Auditions | Audition-fee quote/confirmation, submit audition, read one audition, list clone auditions. |
| Contest | Arena and Promotion Queue reads. |
| Profiles | Owner public profile routes, public clone profile read, and report submit. |
| Discovery Data | Assets, Polymarket search, DefiLlama search, ingestion health/dashboard when used by UI. |
Exclude from the migration:
- service-token worker routes
- internal health routes
- admin moderation routes
- prop admin routes
- future race/ticket/settlement routes that are not frontend-ready
Admin/operator routes can be added to the client spec only when they have a deliberate frontend surface. Service-token and worker routes should stay out of the client API spec. If they become real HTTP integrations between independently deployed processes later, create a separate internal HTTP spec.
Migration Steps
- Add
doc/openapi/client-api.openapi.yaml. - Add OpenAPI validation and typegen scripts.
- Generate
shared/src/generated/client-api.d.ts. - Add frontend API wrappers that use generated operation/schema types.
- Replace frontend imports from
@/lib/shared/*. - Remove
copy:sharedfrom frontend scripts and docs. - Delete old handwritten shared API contract files and unused shared runtime folders.
- Update VitePress sidebar, docs index, frontend README, and repo guidance that references the old shared-copy flow.
- Run verification and commit as one migration PR, preferably with domain-sized commits inside the branch.
Implementation Order
Use commit-sized checkpoints so the migration can be resumed safely after interruption or context compaction:
- Spec and tooling: add
client-api.openapi.yaml, validation/typegen dependencies, and scripts. - Generated contracts: generate
shared/src/generated/client-api.d.tsand expose it fromshared/src/index.ts. - Frontend API wrappers: add typed wrappers under
frontend/src/lib/api/while old shared imports still exist. - Frontend migration: replace
@/lib/shared/*imports with generated types and API wrappers by domain. - Shared cleanup: remove
frontend/src/lib/shared/,copy:shared/setup:shared, old handwritten shared API contracts, and unused legacy shared runtime folders. - Docs and guidance: update VitePress docs, frontend README,
AGENTS.md, andCLAUDE.mdreferences to the old shared-copy flow. - Verification fixes: run required checks and make only follow-up fixes needed for the migration.
Verification
Required before merging the migration:
bash
pnpm run openapi:verify
npm run docs:build
cd frontend && pnpm run lint && pnpm run build
cd backend && npm run typecheck && npm run testIf the full backend suite has an unrelated known failure, record the exact failure in the PR and run the affected route/API smoke tests directly.
Regression Controls
- Preserve existing JSON field names and response envelopes unless a route is intentionally changed.
- Migrate frontend calls by domain: auth, clone, economy, lifecycle, data reads.
- Keep the BFF behavior stable: frontend browser code calls
/api/backend/*, not/api/v1/*. - Keep auth/session routes separate where cookies or SIWE response handling require frontend-specific behavior.
- Do not expose admin, internal, or service-token routes through normal frontend wrappers.
- Do not move backend-only types into
shared/unless another runtime genuinely consumes them. - Remove the old shared contracts only after the generated OpenAPI types compile through the frontend.
Rollback
If generated contracts or OpenAPI shape become too noisy, revert the migration branch before deleting old shared contract files.
Do not partially merge a state where both systems are active without a clear deprecation boundary. The accepted final state should be either:
- old shared contracts still intact, or
- OpenAPI-first contracts fully wired and old shared contracts removed.
Follow-Up Options
After the initial migration is stable:
- Add contract tests that compare backend route responses to the OpenAPI schemas.
- Add a rendered OpenAPI viewer to VitePress.
- Evaluate
openapi-fetchfor a typed frontend client if it fits the BFF cookie/session model. - Add admin/operator routes to the client spec when there is an intentional frontend surface.
- Add a separate internal HTTP spec only if the monolith splits or a real service-token HTTP integration becomes part of the product architecture.