Skip to content

feat(go-api): OpenAPI 3.1 spec and generated client types #72

Description

@Depo-dev

Context

The REST API currently has no machine-readable contract. The request/response shapes exist in Go structs and in hand-written TypeScript Zod schemas in the SDK, and these are already starting to drift slightly. As the API surface grows (pagination cursor, GraphQL, admin endpoints), maintaining consistency manually becomes error-prone.

An OpenAPI 3.1 specification solves this by making the /v1/events API contract the single source of truth. From the spec, we can:

  • Auto-generate the SDK TypeScript types (replacing the hand-written Zod schemas)
  • Generate a Postman collection for developers
  • Auto-generate the REST docs section (instead of hand-writing it)
  • Run spec linting in CI to catch schema drift before it reaches users
  • Enable third-party client generators for Python, Rust, etc.

What needs to be built

api/openapi.yaml

OpenAPI 3.1 document at the repo root api/ directory. The spec must cover:

GET /v1/events

/v1/events:
  get:
    summary: List Soroban events
    operationId: listEvents
    parameters:
      - name: contract_id
        in: query
        schema: { type: string }
        description: Filter by Soroban contract address (strkey format)
      - name: topic_0
        in: query
        schema: { type: string }
      - name: topic_1
        in: query
        schema: { type: string }
      - name: from_ledger
        in: query
        schema: { type: integer, minimum: 0 }
      - name: to_ledger
        in: query
        schema: { type: integer, minimum: 0 }
      - name: network
        in: query
        schema: { type: string, enum: [testnet, mainnet] }
        default: testnet
      - name: first
        in: query
        schema: { type: integer, minimum: 1, maximum: 200, default: 50 }
      - name: after
        in: query
        schema: { type: string }
        description: Opaque pagination cursor from previous response
    responses:
      '200':
        content:
          application/json:
            schema: { : '#/components/schemas/EventListResponse' }
      '400': { : '#/components/responses/BadRequest' }
      '401': { : '#/components/responses/Unauthorized' }
      '503': { : '#/components/responses/ServiceUnavailable' }

GET /v1/events/{id}, GET /v1/health, GET /v1/stats/contracts (#73), GET /v1/stats/indexer (#74), and the admin endpoints (#63) should all be covered.

Reusable schemas (components/schemas):

  • SorobanEvent — the core event object
  • EventListResponse{ events: SorobanEvent[], next_cursor: string | null, total: number }
  • ErrorResponse{ error: { code: string, message: string, request_id: string } }
  • HealthResponse — all fields from the health endpoint

Security scheme:

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
security:
  - ApiKeyAuth: []

SDK type generation

Add a script to sdk/typescript/package.json:

"generate:types": "openapi-typescript ../../api/openapi.yaml -o src/api-types.gen.ts"

The generated file is committed to the repo. The existing hand-written Zod schemas in the SDK are refactored to z.infer<typeof generatedType> where the generated types replace manual schema writing.

CI linting

Add make lint-openapi using Spectral (@stoplight/spectral-cli):

spectral lint api/openapi.yaml --ruleset @stoplight/spectral-oas

This catches: missing operationId, missing description on parameters, response schemas with no content, etc.

Validation in Go API

Use kin-openapi or oapi-codegen to optionally validate requests against the spec at runtime. At minimum, add a CI check that the Go API's actual behavior matches the spec (run the spec through a request validator against the integration test suite).

Acceptance criteria

  • api/openapi.yaml validates with spectral lint (zero errors)
  • All REST endpoints documented (every query param, all response codes including 4xx/5xx)
  • npm run generate:types in the SDK produces types that compile with tsc --noEmit
  • Generated types match the existing SDK's hand-written types (no breaking change to SDK consumers)
  • CI step runs spectral lint and fails the build on spec errors
  • SDK committed api-types.gen.ts is up-to-date (CI fails if it's stale — run generate + git diff check)

Dependencies

Metadata

Metadata

Assignees

Labels

Stellar WaveIssues in the Stellar wave program

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions