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

No one assigned

    Labels

    Stellar WaveIssues in the Stellar wave program

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions