Integrations

OpenAPI

Auto-generated OpenAPI 3.0 spec and interactive API docs from your Redelay modules.

The openapi module introspects every route registered across all modules at startup and emits a complete OpenAPI 3.0.3 specification — with typed schemas, security schemes, validation constraints, and $ref references. A zero-configuration interactive UI is served alongside the raw spec JSON.

How it works

text
Module routes + metadata options
          ↓
    openapi.SchemaRegistry
    (struct reflection → JSON Schema)
          ↓
    openapi.GenerateSpec()
          ↓
  /openapi.json   ← spec
  /reference      ← UI (Scalar / Swagger UI / Redoc)

Every call to router.Handle() carries optional metadata options. The openapi module collects them at startup and generates a complete spec — no annotations, no code generation, no separate YAML files.

Quick start

go
import (
    _ "github.com/redelay/go-framework/modules/openapi"
)

Blank-import registers the module. On first request to /openapi.json the spec is generated and cached. Restart restarts generation.

Endpoint metadata

Declare metadata directly on the route handler with functional options:

go
func (m *Module) Routes(router modules.Router) {
    router.Group("/orders", func(r modules.Router) {

        r.Handle("GET", "/", http.HandlerFunc(m.list),
            modules.Summary("List orders"),
            modules.QueryParam("page",    "integer", "Page number",    "1"),
            modules.QueryParam("page_size","integer","Items per page", "20"),
            modules.Response(200, "Paginated orders", OrderListResponse{}),
            modules.Security("BearerAuth"),
        )

        r.Handle("POST", "/", http.HandlerFunc(m.create),
            modules.Summary("Create order"),
            modules.Body(OrderCreateInput{}),
            modules.Response(201, "Order created",       OrderResponse{}),
            modules.Response(400, "Invalid request",     modules.RedelayErrorResponse{}),
            modules.Response(409, "Duplicate order",     modules.RedelayErrorResponse{}),
            modules.Security("BearerAuth"),
        )

        r.Handle("GET", "/{id}", http.HandlerFunc(m.get),
            modules.Summary("Get order by ID"),
            modules.PathParam("id", "Order ID", "string", "objectid"),
            modules.Response(200, "Order",     OrderResponse{}),
            modules.Response(404, "Not found", modules.RedelayErrorResponse{}),
            modules.Security("BearerAuth"),
        )
    })
}

All metadata options:

OptionDescription
Summary(s)Short operation summary (shown in UI lists)
Description(s)Longer Markdown description
Tags(t...)Group operations — defaults to module name
PathParam(name, desc, type?, format?)Path parameter, auto-marked required
QueryParam(name, type, desc, default?)Query string parameter
Body(model, desc?)Request body — reflects Go struct to JSON Schema
Response(status, desc, model?)Response — reflects model if provided
Security(scheme, scopes...)Security requirement e.g. "BearerAuth"
EndpointDeprecated()Mark operation as deprecated

Schema generation

Go structs are reflected into JSON Schema automatically. No struct tags beyond json: and validate: are needed:

go
type OrderCreateInput struct {
    CustomerID string  `json:"customer_id" validate:"required,uuid4"`
    Items      []Item  `json:"items"       validate:"required,min=1"`
    Currency   string  `json:"currency"    validate:"required,len=3"`
    Notes      *string `json:"notes"`       // pointer → nullable
}

type Item struct {
    ProductID string  `json:"product_id" validate:"required"`
    Quantity  int     `json:"quantity"   validate:"required,min=1"`
    UnitPrice float64 `json:"unit_price" validate:"required,min=0"`
}

Produces:

json
{
  "OrderCreateInput": {
    "type": "object",
    "required": ["customer_id", "items", "currency"],
    "properties": {
      "customer_id": { "type": "string", "format": "uuid" },
      "items": {
        "type": "array",
        "minItems": 1,
        "items": { "$ref": "#/components/schemas/Item" }
      },
      "currency": { "type": "string", "minLength": 3, "maxLength": 3 },
      "notes": { "type": "string", "nullable": true }
    }
  }
}

Tag → schema mapping:

validate tagJSON Schema
requiredfield in required array
emailformat: "email"
uuid4format: "uuid"
min=NminLength: N (string) · minimum: N (number)
max=NmaxLength: N (string) · maximum: N (number)
len=NminLength: N + maxLength: N

Type mapping:

Go typeJSON Schema
stringstring
int, int32integer
float32, float64number
boolboolean
*Ttype of T + nullable: true
[]Tarray + items schema
map[string]Tobject + additionalProperties
time.Timestring + format: "date-time"
primitive.ObjectIDstring + format: "objectid"
Embedded structmerged properties

Security schemes

Configure on the spec itself, not per-route. Schemes declared here are referenced by name in per-route Security() options:

go
import "github.com/redelay/go-framework/openapi"

openapi.GenerateSpec(title, version, registry,
    openapi.WithBearerAuth(),
    // OR:
    openapi.WithOAuth2PasswordFlow("/api/v1/auth/login", map[string]string{
        "read":  "Read access",
        "write": "Write access",
    }),
)

WithOAuth2PasswordFlow enables Swagger UI's built-in Authorize popup — testers can enter username and password directly in the UI without copying tokens manually.

UI providers

The OPENAPI_UI environment variable selects the documentation UI:

ValueUIBest for
scalar (default)ScalarClean, modern look
swaggerSwagger UIOAuth2 Authorize flow, familiar
redocRedocRead-heavy reference docs
disabled—Spec-only (headless services)

All UIs are served from a CDN build embedded in the binary — no Node.js, no build step.

Schema reference diagram

text
Go struct (OrderCreateInput)
        │
        ▼
SchemaRegistry.Register()
        │
        ▼
components/schemas/OrderCreateInput   ← named, reusable
        │
        ├── referenced by POST /orders requestBody
        ├── referenced by GET  /orders/{id} 200 response
        └── referenced by inline Item sub-schema

Named structs appear once in components/schemas and are referenced via $ref everywhere they are used. Primitive and anonymous types are inlined.

Built-in module spec preview

When all built-in modules are loaded, the generated spec includes:

text
POST   /api/v1/auth/login         Auth — Login
POST   /api/v1/auth/refresh       Auth — Refresh token
POST   /api/v1/auth/revoke        Auth — Revoke token
GET    /api/v1/users/me           Users — Current user profile
GET    /api/v1/users/             Users — List users
POST   /api/v1/users/             Users — Create user
GET    /api/v1/users/{id}         Users — Get user
PATCH  /api/v1/users/{id}         Users — Update user
DELETE /api/v1/users/{id}         Users — Delete user
GET    /api/v1/health             Health — Health check

All schemas (UserResponse, AuthTokenResponse, RedelayErrorResponse, …) are in components/schemas and referenced via $ref.

Modspec integration

The modspec browser at /modules links directly to /reference in its top navigation bar, so your team can navigate from the architecture view straight to the interactive API docs without leaving the browser.

Configuration

VariableDefaultDescription
OPENAPI_UIscalarUI provider (scalar, swagger, redoc, disabled)
OPENAPI_SPEC_PATH/openapi.jsonURL path for the JSON spec
OPENAPI_UI_PATH/referenceURL path for the interactive UI
APP_NAMEredelayValue of info.title in the spec
APP_VERSION1.0.0Value of info.version in the spec

Programmatic spec generation

The openapi package can generate specs outside of HTTP context — useful for CI checks, contract testing, or publishing to API gateways:

go
import (
    "github.com/redelay/go-framework/openapi"
    "github.com/redelay/go-framework/modules"
)

registry := openapi.NewSchemaRegistry()

spec := openapi.GenerateSpec("My Service", "1.0.0", registry,
    openapi.WithBearerAuth(),
)

// Write to file
f, _ := os.Create("openapi.json")
json.NewEncoder(f).Encode(spec)