Skip to content

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 pathFrontend routeBackend 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

PathPurpose
doc/openapi/client-api.openapi.yamlCanonical OpenAPI 3.1 spec for client-facing HTTP routes.
shared/src/generated/client-api.d.tsGenerated TypeScript types from the OpenAPI spec.
shared/src/index.tsRe-export generated contract types.
frontend/src/lib/api/Frontend API client wrappers that call /api/backend/* and /api/auth/*.
doc/client-api-contract.mdHuman route-family reference backed by the OpenAPI contract.
backend/src/api/*HTTP route adapters typed against generated OpenAPI operations.
backend/src/modules/*/contracts.tsOptional 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:shared scripts
  • handwritten shared/src/*/api-contract.ts files
  • legacy shared/src/markets, shared/src/supabase, and other unused runtime helpers

Contract Ownership

Contract areaSource of truth after migration
HTTP paths, methods, auth mode, request bodies, query params, and response shapesOpenAPI
Generated TypeScript operation/schema typesshared/src/generated/client-api.d.ts
Frontend fetch behavior, cookie/session refresh, and /api/backend/* routingFrontend BFF route handlers and API wrappers
Backend HTTP request/response adapter typesGenerated OpenAPI operation/schema types imported by backend/src/api/* only
Backend in-process function/module contractsOwning backend module
Backend business rules and data persistenceBackend modules
Product vocabulary and lifecycle rulesProduct/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 surfaceInclude
Auth/sessionSIWE challenge/verify, refresh, logout, current user.
ClonesList/read/update clones and entitlement read.
WorkflowStrategy draft/version/publish, backtest jobs, decision history, and workflow data previews.
AuditionsAudition-fee quote/confirmation, submit audition, read one audition, list clone auditions.
ContestArena and Promotion Queue reads.
ProfilesOwner public profile routes, public clone profile read, and report submit.
Discovery DataAssets, 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

  1. Add doc/openapi/client-api.openapi.yaml.
  2. Add OpenAPI validation and typegen scripts.
  3. Generate shared/src/generated/client-api.d.ts.
  4. Add frontend API wrappers that use generated operation/schema types.
  5. Replace frontend imports from @/lib/shared/*.
  6. Remove copy:shared from frontend scripts and docs.
  7. Delete old handwritten shared API contract files and unused shared runtime folders.
  8. Update VitePress sidebar, docs index, frontend README, and repo guidance that references the old shared-copy flow.
  9. 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:

  1. Spec and tooling: add client-api.openapi.yaml, validation/typegen dependencies, and scripts.
  2. Generated contracts: generate shared/src/generated/client-api.d.ts and expose it from shared/src/index.ts.
  3. Frontend API wrappers: add typed wrappers under frontend/src/lib/api/ while old shared imports still exist.
  4. Frontend migration: replace @/lib/shared/* imports with generated types and API wrappers by domain.
  5. Shared cleanup: remove frontend/src/lib/shared/, copy:shared/setup:shared, old handwritten shared API contracts, and unused legacy shared runtime folders.
  6. Docs and guidance: update VitePress docs, frontend README, AGENTS.md, and CLAUDE.md references to the old shared-copy flow.
  7. 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 test

If 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-fetch for 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.