Skip to content

feat(go-api): GraphQL endpoint with schema mirroring REST #66

Description

@Depo-dev

Context

Phase 2 of the Trident PRD requires a GraphQL endpoint at /graphql. GraphQL is increasingly the preferred query interface for blockchain indexers (The Graph pioneered this, and it has become the standard) because it allows clients to request exactly the fields they need, compose complex filters in a single request, and use the same endpoint for both queries and subscriptions.

Adding GraphQL does not replace the REST API — both stay available. GraphQL is additive and targets developers who prefer it for its flexibility and self-documenting schema.

What needs to be built

Schema design

scalar JSON
scalar DateTime

enum EventType {
  CONTRACT
  SYSTEM
  DIAGNOSTIC
}

type SorobanEvent {
  id: ID!
  contractId: String!
  ledgerSequence: Int!
  ledgerTimestamp: DateTime!
  topic0: String
  topic1: String
  topic2: String
  topic3: String
  data: JSON
  txHash: String!
  eventType: EventType!
  network: String!
}

type PageInfo {
  hasNextPage: Boolean!
  endCursor: String
}

type EventEdge {
  node: SorobanEvent!
  cursor: String!
}

type EventConnection {
  edges: [EventEdge!]!
  pageInfo: PageInfo!
  totalCount: Int
}

type Query {
  events(
    contractId: String
    topic0: String
    topic1: String
    fromLedger: Int
    toLedger: Int
    network: String
    first: Int
    after: String
  ): EventConnection!

  event(id: ID!): SorobanEvent
}

The EventConnection type follows the Relay Cursor Connection Specification — this is the de facto standard for GraphQL pagination and what Apollo Client, urql, and Relay all expect out of the box.

Library recommendation

99designs/gqlgen — schema-first, generates type-safe Go resolvers from the GraphQL SDL. The generated types eliminate a whole class of bugs where the Go struct and the schema drift apart. Alternatively, graph-gophers/graphql-go for a no-code-gen approach.

Resolver implementation

events resolver:

event resolver:

  • Calls gRPC GetEvent
  • Returns null (not an error) when not found, per GraphQL convention

Authentication

Same X-API-Key header check as REST. The existing middleware should apply to /graphql without modification.

Error handling

GraphQL errors go in the errors array in the response body, NOT as HTTP 4xx/5xx. The HTTP status for a GraphQL request is almost always 200 (even for application errors). The only time to return non-200 is a malformed JSON body (400) or auth failure (401).

Transport

POST /graphql with Content-Type: application/json body:

{ "query": "{ events(contractId: \"C...\") { edges { node { id txHash } } } }" }

Also support GET /graphql?query=...&variables=... for simple queries (required by some GraphQL clients).

Acceptance criteria

  • POST /graphql resolves events query with all filter combinations (contract, topic0, ledger range, network, pagination)
  • POST /graphql resolves event query by ID, returns null (not error) for missing ID
  • Pagination cursor from pageInfo.endCursor produces correct next page when passed as after
  • Authentication: missing or invalid API key returns HTTP 401, not a GraphQL error
  • GraphQL introspection works ({ __schema { types { name } } })
  • Response for a field error (e.g. gRPC unavailable) follows the errors array format
  • Integration test with real DB and mock gRPC
  • Linted and type-checked (if using gqlgen, generated code must be committed)

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