Skip to content

Navigation Menu

Sign in
Sign up

Repository files navigation

Rex OpenAPI Extension (rextension-openapi)

A comprehensive OpenAPI 3.1 specification generator extension for the Rex framework.

Go Version Coverage License

Overview

rextension-openapi is a Rex extension that provides:

  • Automatic OpenAPI 3.1 document generation from registered routes
  • Rich JSON Schema generation via reflection on Go types
  • Struct tag support: json, validate (required, min, max, etc.)
  • Union type support: OneOf/AnyOf/AllOf via validation.BodySchema
  • Per-status-code response documentation via ValidatableRoute.Responses()
  • Security scheme documentation (soft-dependency on rextension-security)
  • Request/response examples via ResponseExamplesProvider and RequestBodyExamplesProvider
  • Response descriptions via ResponseDescriptionProvider
  • Generated once at startup, when the route table is frozen — a generation failure is a startup error, not a 500 on the first request
  • Router scoping: which routers' routes appear in the document, and which router serves it
  • Configurable serve path (default: /openapi.json)

Installation

go get github.com/kryovyx/rextension-openapi

Quick Start

package main
import (
 "github.com/kryovyx/rex"
 "github.com/kryovyx/rex/route"
 openapi "github.com/kryovyx/rextension-openapi"
)
func main() {
 app := rex.New()
 // Add OpenAPI extension with default config
 app.WithOptions(
 openapi.WithOpenAPI(nil),
 )
 // Register your routes (implementing OpenAPIRoute)
 app.RegisterRoute(&HelloRoute{})
 // Run the application
 // OpenAPI document available at /openapi.json
 if err := app.Run(); err != nil {
 panic(err)
 }
}

OpenAPIRoute Interface

Routes must implement the OpenAPIRoute interface to be included in the generated OpenAPI document. Routes that do not implement this interface are excluded from the spec.

type OpenAPIRoute interface {
 OperationID() string
 Summary() string
 Description() string
 Tags() []string
}

A route must also implement route.Route (Method(), Path(), Handler()). Combining both interfaces provides the metadata needed for the OpenAPI spec:

type HelloRoute struct{}
func (r *HelloRoute) Method() string { return "GET" }
func (r *HelloRoute) Path() string { return "/hello" }
func (r *HelloRoute) Handler() route.HandlerFunc {
 return func(ctx route.Context) {
 ctx.JSON(200, map[string]string{"message": "Hello, World!"})
 }
}
// OpenAPIRoute implementation
func (r *HelloRoute) OperationID() string { return "getHello" }
func (r *HelloRoute) Summary() string { return "Say hello" }
func (r *HelloRoute) Description() string { return "Returns a greeting message" }
func (r *HelloRoute) Tags() []string { return []string{"greetings"} }

Request/Response Schemas

If a route also implements ValidatableRoute (from rextension-validation), request and response schemas are automatically generated from the Go types via reflection.

Request Body Schema

Struct tags drive schema generation:

type CreateUserRequest struct {
 Name string `json:"name" validate:"required,min=1,max=100"`
 Email string `json:"email" validate:"required"`
 Age int `json:"age" validate:"min=0,max=150"`
}

This produces an OpenAPI schema with required fields and minLength/maxLength/minimum/maximum constraints.

Union Types

Use validation.BodySchema to declare union types:

  • OneOf: Exactly one schema matches
  • AnyOf: One or more schemas match
  • AllOf: All schemas must match

Per-Status-Code Responses

Routes implementing ValidatableRoute can return Responses() to document each status code with its own schema type:

func (r *MyRoute) Responses() map[int]interface{} {
 return map[int]interface{}{
 200: SuccessResponse{},
 400: ErrorResponse{},
 401: AuthErrorResponse{},
 }
}

Security Integration

Routes that implement both OpenAPIRoute and the SecuredRoute interface (from rextension-security) automatically have security requirements added to their operations in the spec.

func (r *ProtectedRoute) RequiredSchemes() []string {
 return []string{"bearer"}
}

Security schemes registered via rextension-security are discovered through the DI container and the rextension global registry, and appear in the components/securitySchemes section of the document.

Examples and Descriptions

Response Examples

Implement ResponseExamplesProvider to supply named examples per status code:

func (r *MyRoute) ResponseExamples() map[int]map[string]openapi.ExampleObject {
 return map[int]map[string]openapi.ExampleObject{
 200: {
 "success": {Summary: "Successful payment", Value: PaymentResponse{Status: "success"}},
 },
 }
}

Request Body Examples

Implement RequestBodyExamplesProvider to supply named examples for the request body:

func (r *MyRoute) RequestBodyExamples() map[string]openapi.ExampleObject {
 return map[string]openapi.ExampleObject{
 "usd-payment": {Summary: "USD payment", Value: PaymentRequest{Amount: 1000, Currency: "USD"}},
 }
}

Response Descriptions

Implement ResponseDescriptionProvider to supply human-readable descriptions per status code:

func (r *MyRoute) ResponseDescriptions() map[int]string {
 return map[int]string{
 200: "Payment processed successfully",
 400: "Invalid request payload",
 401: "Missing or invalid authentication",
 }
}

Routers and base paths

A route's documented path is the router's BaseURL plus the route's own path. A route declared as /schemas on a router based at /internal is served at /internal/schemas, and that is what the document says — the router strips the prefix before matching, so the route itself declares the bare path.

Which routers contribute routes is configuration, and the default is deliberately narrow: the default router alone. A document that omits a route can be widened; a document that published a private route has already published it.

openapi.NewConfig(
 openapi.WithIncludeRouters("*"), // every router
 openapi.WithExcludeRouters("internal"), // except this one
)

ExcludeRouters is applied after IncludeRouters, so it wins over "*".

Two routes that resolve to the same method and documented path are a startup error, naming both operations and both routers. They used to overwrite one another, so an operation simply vanished from the document.

More than one document

One instance of the extension serves one document. An application that wants a public document on its public listener and a full one on its internal listener runs two instances — each with its own name, its own router scope, and its own ServeOnRouter:

rex.New(
 // Public: the default router only, served on the default listener.
 openapi.WithOpenAPI(openapi.NewConfig(
 openapi.WithName("public"),
 openapi.WithTitle("Public API"),
 )),
 // Internal: everything, served on the internal listener only.
 openapi.WithOpenAPI(openapi.NewConfig(
 openapi.WithName("internal"),
 openapi.WithTitle("Internal API"),
 openapi.WithIncludeRouters("*"),
 openapi.WithServeOnRouter("internal"),
 )),
 // The UI beside the internal document.
 swagger.WithSwagger(swagger.NewConfig(
 swagger.WithServeOnRouter("internal"),
 )),
)

Name is what tells the two apart in the logs and in startup errors; both instances would otherwise report identically.

Configuration Reference

Field Type Default Description
Name string "openapi" Instance name in log lines and startup errors
Title string "API" API title in the info section
Version string "1.0.0" API version in the info section
Description string "" API description in the info section
ServePath string "/openapi.json" Path to serve the OpenAPI JSON document
Tags []Tag nil Top-level tag descriptions
IncludeRouters []string nil Routers whose routes appear; empty = default router, "*" = all
ExcludeRouters []string nil Routers to omit, applied after IncludeRouters
ServeOnRouter string "" Router that serves the document; empty = default router

Config Options

openapi.WithOpenAPI(openapi.NewConfig(
 openapi.WithName("public"),
 openapi.WithTitle("My API"),
 openapi.WithVersion("2.0.0"),
 openapi.WithDescription("My awesome API"),
 openapi.WithServePath("/docs/openapi.json"),
))
Option Description
WithName(s) Names the instance in logs and startup errors
WithTitle(s) Sets the API title
WithVersion(s) Sets the API version
WithDescription(s) Sets the API description
WithServePath(s) Sets the path to serve the OpenAPI JSON
WithTags(...) Registers top-level tag descriptions
WithIncludeRouters(...) Routers whose routes appear in the document
WithExcludeRouters(...) Routers to omit, applied after the include list
WithServeOnRouter(s) Router that serves the document

Best Practices

  1. Implement OpenAPIRoute on all public routes: This ensures complete API documentation is generated automatically
  2. Use meaningful operation IDs: Follow a consistent naming convention like getUser, createOrder for clear client SDK generation
  3. Leverage struct tags: Use json and validate tags on request/response types for accurate schema generation
  4. Provide response examples: Implement ResponseExamplesProvider to give consumers concrete examples of your API responses
  5. Add response descriptions: Use ResponseDescriptionProvider to document what each status code means in your domain
  6. Declare per-status-code responses: Return all possible response types from Responses() for complete documentation
  7. Use tags to organize operations: Group related endpoints under the same tags for better navigation
  8. Keep the internal API in its own document: run a second instance with WithIncludeRouters and WithServeOnRouter rather than widening the public one
  9. Customize the serve path: In production, consider serving the spec at a well-known path like /openapi.json or /docs/openapi.json

Upgrading

v0.2.1 → v0.3.0. MIGRATION.md is the upgrade guide for this module, written to stand alone — it carries the dependency-ordered go get sequence, every breaking change here, and what to verify afterwards. Other modules of the framework each have their own; that file links to them.

Contributing

The framework is in alpha, and external contributions open at v1.0.0. Until then pull requests will be closed unmerged — but issues are very welcome. Bug reports, questions and feature requests all feed into what v1.0.0 looks like.

See CONTRIBUTING.md for the rules that will apply, and COMMIT-CONVENTIONS.md for the commit format.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Copyright

© 2026 Kryovyx

About

OpenAPI 3.1 specification generation for the REX framework. Generated from the route table itself — no reflection over handlers, no annotations, no build step.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /