Skip to content

OpenAPI

The OpenAPI extension (ext/openapi) integrates relay with OpenAPI 3.x specifications, enabling auto-generated clients, request validation, and spec-driven testing.

Installation

go get github.com/jhonsferg/relay/ext/openapi

Loading a spec

import (
    "github.com/jhonsferg/relay"
    "github.com/jhonsferg/relay/ext/openapi"
)

spec, err := openapi.LoadFile("api/openapi.yaml")
if err != nil {
    log.Fatal(err)
}

client := relay.New(relay.Config{
    BaseURL: spec.Servers[0].URL,
})

// Wrap client with OpenAPI middleware
oa := openapi.New(client, spec)

Request validation

Validate outgoing requests against the spec before sending:

oa := openapi.New(client, spec, openapi.Options{
    ValidateRequests: true,
})

var user User
_, err := oa.R().
    SetResult(&user).
    GET(ctx, "/users/123")
// Returns openapi.ErrValidation if path, query params, or body
// do not match the spec definition

Response validation

Validate incoming responses:

oa := openapi.New(client, spec, openapi.Options{
    ValidateRequests:  true,
    ValidateResponses: true,
    StrictMode:        false, // warn instead of error on unknown fields
})

Typed operations

Generate a typed wrapper directly from the spec at build time:

go run github.com/jhonsferg/relay/ext/openapi/cmd/gen \
    --spec api/openapi.yaml \
    --out gen/client.go \
    --package gen

The generated client exposes typed methods per operation:

import "myapp/gen"

c := gen.NewClient(relay.New(relay.Config{BaseURL: "https://api.example.com"}))

users, err := c.ListUsers(ctx, gen.ListUsersParams{
    Page:  1,
    Limit: 20,
})

Spec-driven mock

Use the spec to auto-generate mock responses during tests:

import "github.com/jhonsferg/relay/ext/openapi"

mockTransport := openapi.NewMockFromSpec(spec, openapi.MockOptions{
    UseExamples:  true,  // prefer spec examples
    GenerateFake: true,  // generate fake data when no example present
})

client := relay.New(relay.Config{Transport: mockTransport})

Dynamic base URL selection

Select a server from the spec by environment name:

spec, _ := openapi.LoadFile("openapi.yaml")

// spec.Servers may include prod, staging, sandbox URLs with x-environment tags
client := relay.New(relay.Config{
    BaseURL: spec.ServerByEnv("staging").URL,
})

Validation error handling

_, err := oa.R().POST(ctx, "/users", invalidPayload)
var ve *openapi.ValidationError
if errors.As(err, &ve) {
    for _, issue := range ve.Issues {
        log.Printf("field %q: %s", issue.Path, issue.Message)
    }
}

See also