openapi

package
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 30, 2025 License: MIT Imports: 11 Imported by: 0

README

OpenAPI Generator for ResolveSpec

This package provides automatic OpenAPI 3.0 specification generation for ResolveSpec, RestheadSpec, and FuncSpec API frameworks.

Features

  • Automatic Schema Generation: Generates OpenAPI schemas from Go struct models
  • Multiple Framework Support: Works with RestheadSpec, ResolveSpec, and FuncSpec
  • Dynamic Endpoint Discovery: Automatically discovers all registered models and generates paths
  • Query Parameter Access: Access spec via ?openapi on any endpoint or via /openapi
  • Comprehensive Documentation: Includes all request/response schemas, parameters, and security schemes

Quick Start

RestheadSpec Example
import (
    "github.com/bitechdev/ResolveSpec/pkg/openapi"
    "github.com/bitechdev/ResolveSpec/pkg/restheadspec"
    "github.com/gorilla/mux"
)

func main() {
    // 1. Create handler
    handler := restheadspec.NewHandlerWithGORM(db)

    // 2. Register models
    handler.registry.RegisterModel("public.users", User{})
    handler.registry.RegisterModel("public.products", Product{})

    // 3. Configure OpenAPI generator
    handler.SetOpenAPIGenerator(func() (string, error) {
        generator := openapi.NewGenerator(openapi.GeneratorConfig{
            Title:       "My API",
            Description: "API documentation",
            Version:     "1.0.0",
            BaseURL:     "http://localhost:8080",
            Registry:    handler.registry.(*modelregistry.DefaultModelRegistry),
            IncludeRestheadSpec: true,
            IncludeResolveSpec:  false,
            IncludeFuncSpec:     false,
        })
        return generator.GenerateJSON()
    })

    // 4. Setup routes (automatically includes /openapi endpoint)
    router := mux.NewRouter()
    restheadspec.SetupMuxRoutes(router, handler, nil)

    // Start server
    http.ListenAndServe(":8080", router)
}
ResolveSpec Example
func main() {
    // 1. Create handler
    handler := resolvespec.NewHandlerWithGORM(db)

    // 2. Register models
    handler.RegisterModel("public", "users", User{})
    handler.RegisterModel("public", "products", Product{})

    // 3. Configure OpenAPI generator
    handler.SetOpenAPIGenerator(func() (string, error) {
        generator := openapi.NewGenerator(openapi.GeneratorConfig{
            Title:       "My API",
            Version:     "1.0.0",
            Registry:    handler.registry.(*modelregistry.DefaultModelRegistry),
            IncludeResolveSpec: true,
        })
        return generator.GenerateJSON()
    })

    // 4. Setup routes
    router := mux.NewRouter()
    resolvespec.SetupMuxRoutes(router, handler, nil)

    http.ListenAndServe(":8080", router)
}

Accessing the OpenAPI Specification

Once configured, the OpenAPI spec is available in two ways:

1. Global /openapi Endpoint
curl http://localhost:8080/openapi

Returns the complete OpenAPI specification for all registered models.

2. Query Parameter on Any Endpoint
# RestheadSpec
curl http://localhost:8080/public/users?openapi

# ResolveSpec
curl http://localhost:8080/resolve/public/users?openapi

Returns the same OpenAPI specification as /openapi.

Generated Endpoints

RestheadSpec

For each registered model (e.g., public.users), the following paths are generated:

  • GET /public/users - List records with header-based filtering
  • POST /public/users - Create a new record
  • GET /public/users/{id} - Get a single record
  • PUT /public/users/{id} - Update a record
  • PATCH /public/users/{id} - Partially update a record
  • DELETE /public/users/{id} - Delete a record
  • GET /public/users/metadata - Get table metadata
  • OPTIONS /public/users - CORS preflight
ResolveSpec

For each registered model (e.g., public.users), the following paths are generated:

  • POST /resolve/public/users - Execute operations (read, create, meta)
  • POST /resolve/public/users/{id} - Execute operations (update, delete)
  • GET /resolve/public/users - Get metadata
  • OPTIONS /resolve/public/users - CORS preflight

Schema Generation

The generator automatically extracts information from your Go struct tags:

type User struct {
    ID        int       `json:"id" gorm:"primaryKey" description:"User ID"`
    Name      string    `json:"name" gorm:"not null" description:"User's full name"`
    Email     string    `json:"email" gorm:"unique" description:"Email address"`
    CreatedAt time.Time `json:"created_at" description:"Creation timestamp"`
    Roles     []string  `json:"roles" description:"User roles"`
}

This generates an OpenAPI schema with:

  • Property names from json tags
  • Required fields from gorm:"not null" and non-pointer types
  • Descriptions from description tags
  • Proper type mappings (int → integer, time.Time → string with format: date-time, etc.)

RestheadSpec Headers

The generator documents all RestheadSpec HTTP headers:

  • X-Filters - JSON array of filter conditions
  • X-Columns - Comma-separated columns to select
  • X-Sort - JSON array of sort specifications
  • X-Limit - Maximum records to return
  • X-Offset - Records to skip
  • X-Preload - Relations to eager load
  • X-Expand - Relations to expand (LEFT JOIN)
  • X-Distinct - Enable DISTINCT queries
  • X-Response-Format - Response format (detail, simple, syncfusion)
  • X-Clean-JSON - Remove null/empty fields
  • X-Custom-SQL-Where - Custom WHERE clause (AND)
  • X-Custom-SQL-Or - Custom WHERE clause (OR)

ResolveSpec Request Body

The generator documents the ResolveSpec request body structure:

{
  "operation": "read",
  "data": {},
  "id": 123,
  "options": {
    "limit": 10,
    "offset": 0,
    "filters": [
      {"column": "status", "operator": "eq", "value": "active"}
    ],
    "sort": [
      {"column": "created_at", "direction": "desc"}
    ]
  }
}

Security Schemes

The generator automatically includes common security schemes:

  • BearerAuth: JWT Bearer token authentication
  • SessionToken: Session token in Authorization header
  • CookieAuth: Cookie-based session authentication
  • HeaderAuth: Header-based user authentication (X-User-ID)

FuncSpec Custom Endpoints

For FuncSpec, you can manually register custom SQL endpoints:

funcSpecEndpoints := map[string]openapi.FuncSpecEndpoint{
    "/api/reports/sales": {
        Path:        "/api/reports/sales",
        Method:      "GET",
        Summary:     "Get sales report",
        Description: "Returns sales data for specified date range",
        SQLQuery:    "SELECT * FROM sales WHERE date BETWEEN [start_date] AND [end_date]",
        Parameters:  []string{"start_date", "end_date"},
    },
}

generator := openapi.NewGenerator(openapi.GeneratorConfig{
    // ... other config
    IncludeFuncSpec:   true,
    FuncSpecEndpoints: funcSpecEndpoints,
})

Combining Multiple Frameworks

You can generate a unified OpenAPI spec that includes multiple frameworks:

generator := openapi.NewGenerator(openapi.GeneratorConfig{
    Title:       "Unified API",
    Version:     "1.0.0",
    Registry:    sharedRegistry,
    IncludeRestheadSpec: true,
    IncludeResolveSpec:  true,
    IncludeFuncSpec:     true,
    FuncSpecEndpoints:   funcSpecEndpoints,
})

This will generate a complete spec with all endpoints from all frameworks.

Advanced Customization

You can customize the generated spec further:

handler.SetOpenAPIGenerator(func() (string, error) {
    generator := openapi.NewGenerator(config)

    // Generate initial spec
    spec, err := generator.Generate()
    if err != nil {
        return "", err
    }

    // Add contact information
    spec.Info.Contact = &openapi.Contact{
        Name:  "API Support",
        Email: "support@example.com",
        URL:   "https://example.com/support",
    }

    // Add additional servers
    spec.Servers = append(spec.Servers, openapi.Server{
        URL:         "https://staging.example.com",
        Description: "Staging Server",
    })

    // Convert back to JSON
    data, _ := json.MarshalIndent(spec, "", "  ")
    return string(data), nil
})

Using the Built-in UI Handler

The package includes a built-in UI handler that serves popular OpenAPI visualization tools. No need to download or manage static files - everything is served from CDN.

Quick Start
import (
    "github.com/bitechdev/ResolveSpec/pkg/openapi"
    "github.com/gorilla/mux"
)

func main() {
    router := mux.NewRouter()

    // Setup your API routes and OpenAPI generator...
    // (see examples above)

    // Add the UI handler - defaults to Swagger UI
    openapi.SetupUIRoute(router, "/docs", openapi.UIConfig{
        UIType:  openapi.SwaggerUI,
        SpecURL: "/openapi",
        Title:   "My API Documentation",
    })

    // Now visit http://localhost:8080/docs
    http.ListenAndServe(":8080", router)
}
Supported UI Frameworks

The handler supports four popular OpenAPI UI frameworks:

1. Swagger UI (Default)

The most widely used OpenAPI UI with excellent compatibility and features.

openapi.SetupUIRoute(router, "/docs", openapi.UIConfig{
    UIType: openapi.SwaggerUI,
    Theme:  "dark", // optional: "light" or "dark"
})
2. RapiDoc

Modern, customizable, and feature-rich OpenAPI UI.

openapi.SetupUIRoute(router, "/docs", openapi.UIConfig{
    UIType: openapi.RapiDoc,
    Theme:  "dark",
})
3. Redoc

Clean, responsive documentation with great UX.

openapi.SetupUIRoute(router, "/docs", openapi.UIConfig{
    UIType: openapi.Redoc,
})
4. Scalar

Modern and sleek OpenAPI documentation.

openapi.SetupUIRoute(router, "/docs", openapi.UIConfig{
    UIType: openapi.Scalar,
    Theme:  "dark",
})
Configuration Options
type UIConfig struct {
    UIType     UIType // SwaggerUI, RapiDoc, Redoc, or Scalar
    SpecURL    string // URL to OpenAPI spec (default: "/openapi")
    Title      string // Page title (default: "API Documentation")
    FaviconURL string // Custom favicon URL (optional)
    CustomCSS  string // Custom CSS to inject (optional)
    Theme      string // "light" or "dark" (support varies by UI)
}
Custom Styling Example
openapi.SetupUIRoute(router, "/docs", openapi.UIConfig{
    UIType: openapi.SwaggerUI,
    Title:  "Acme Corp API",
    CustomCSS: `
        .swagger-ui .topbar {
            background-color: #1976d2;
        }
        .swagger-ui .info .title {
            color: #1976d2;
        }
    `,
})
Using Multiple UIs

You can serve different UIs at different paths:

// Swagger UI at /docs
openapi.SetupUIRoute(router, "/docs", openapi.UIConfig{
    UIType: openapi.SwaggerUI,
})

// Redoc at /redoc
openapi.SetupUIRoute(router, "/redoc", openapi.UIConfig{
    UIType: openapi.Redoc,
})

// RapiDoc at /api-docs
openapi.SetupUIRoute(router, "/api-docs", openapi.UIConfig{
    UIType: openapi.RapiDoc,
})
Manual Handler Usage

If you need more control, use the handler directly:

handler := openapi.UIHandler(openapi.UIConfig{
    UIType:  openapi.SwaggerUI,
    SpecURL: "/api/openapi.json",
})

router.Handle("/documentation", handler)

Using with External Swagger UI

Alternatively, you can use an external Swagger UI instance:

  1. Get the spec from /openapi
  2. Load it in Swagger UI at https://petstore.swagger.io/
  3. Or self-host Swagger UI and point it to your /openapi endpoint

Testing

You can test the OpenAPI endpoint:

# Get the full spec
curl http://localhost:8080/openapi | jq

# Validate with openapi-generator
openapi-generator validate -i http://localhost:8080/openapi

# Generate client SDKs
openapi-generator generate -i http://localhost:8080/openapi -g typescript-fetch -o ./client

Complete Example

See example.go in this package for complete, runnable examples including:

  • Basic RestheadSpec setup
  • Basic ResolveSpec setup
  • Combining both frameworks
  • Adding FuncSpec endpoints
  • Advanced customization

License

Part of the ResolveSpec project.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExampleBothSpecs

func ExampleBothSpecs(db *gorm.DB)

ExampleBothSpecs shows how to combine both RestheadSpec and ResolveSpec

func ExampleCustomization

func ExampleCustomization()

ExampleCustomization shows advanced customization options

func ExampleResolveSpec

func ExampleResolveSpec(db *gorm.DB)

ExampleResolveSpec shows how to configure OpenAPI generation for ResolveSpec

func ExampleRestheadSpec

func ExampleRestheadSpec(db *gorm.DB)

ExampleRestheadSpec shows how to configure OpenAPI generation for RestheadSpec

func ExampleWithFuncSpec

func ExampleWithFuncSpec()

ExampleWithFuncSpec shows how to add FuncSpec endpoints to OpenAPI

func ExampleWithUIHandler added in v0.0.120

func ExampleWithUIHandler(db *gorm.DB)

ExampleWithUIHandler shows how to serve OpenAPI documentation with a web UI

func SetupUIRoute added in v0.0.120

func SetupUIRoute(router *mux.Router, path string, config UIConfig)

SetupUIRoute adds the OpenAPI UI route to a mux router This is a convenience function for the most common use case

func UIHandler added in v0.0.120

func UIHandler(config UIConfig) http.HandlerFunc

UIHandler creates an HTTP handler that serves an OpenAPI UI

Types

type Components

type Components struct {
	Schemas         map[string]Schema         `json:"schemas,omitempty"`
	SecuritySchemes map[string]SecurityScheme `json:"securitySchemes,omitempty"`
}

type Contact

type Contact struct {
	Name  string `json:"name,omitempty"`
	URL   string `json:"url,omitempty"`
	Email string `json:"email,omitempty"`
}

type FuncSpecEndpoint

type FuncSpecEndpoint struct {
	Path        string
	Method      string
	Summary     string
	Description string
	SQLQuery    string
	Parameters  []string // Parameter names extracted from SQL
}

FuncSpecEndpoint represents a FuncSpec endpoint for OpenAPI generation

type Generator

type Generator struct {
	// contains filtered or unexported fields
}

Generator creates OpenAPI specifications

func NewGenerator

func NewGenerator(config GeneratorConfig) *Generator

NewGenerator creates a new OpenAPI generator

func (*Generator) Generate

func (g *Generator) Generate() (*OpenAPISpec, error)

Generate creates the complete OpenAPI specification

func (*Generator) GenerateJSON

func (g *Generator) GenerateJSON() (string, error)

GenerateJSON generates OpenAPI spec as JSON string

type GeneratorConfig

type GeneratorConfig struct {
	Title               string
	Description         string
	Version             string
	BaseURL             string
	Registry            *modelregistry.DefaultModelRegistry
	IncludeRestheadSpec bool
	IncludeResolveSpec  bool
	IncludeFuncSpec     bool
	FuncSpecEndpoints   map[string]FuncSpecEndpoint // path -> endpoint info
}

GeneratorConfig holds configuration for OpenAPI spec generation

type Info

type Info struct {
	Title       string   `json:"title"`
	Description string   `json:"description,omitempty"`
	Version     string   `json:"version"`
	Contact     *Contact `json:"contact,omitempty"`
}

type MediaType

type MediaType struct {
	Schema  *Schema     `json:"schema,omitempty"`
	Example interface{} `json:"example,omitempty"`
}

type OpenAPISpec

type OpenAPISpec struct {
	OpenAPI    string                `json:"openapi"`
	Info       Info                  `json:"info"`
	Servers    []Server              `json:"servers,omitempty"`
	Paths      map[string]PathItem   `json:"paths"`
	Components Components            `json:"components"`
	Security   []map[string][]string `json:"security,omitempty"`
}

OpenAPISpec represents the OpenAPI 3.0 specification structure

type Operation

type Operation struct {
	Summary     string                `json:"summary,omitempty"`
	Description string                `json:"description,omitempty"`
	OperationID string                `json:"operationId,omitempty"`
	Tags        []string              `json:"tags,omitempty"`
	Parameters  []Parameter           `json:"parameters,omitempty"`
	RequestBody *RequestBody          `json:"requestBody,omitempty"`
	Responses   map[string]Response   `json:"responses"`
	Security    []map[string][]string `json:"security,omitempty"`
}

type Parameter

type Parameter struct {
	Name        string      `json:"name"`
	In          string      `json:"in"` // "query", "header", "path", "cookie"
	Description string      `json:"description,omitempty"`
	Required    bool        `json:"required,omitempty"`
	Schema      *Schema     `json:"schema,omitempty"`
	Example     interface{} `json:"example,omitempty"`
}

type PathItem

type PathItem struct {
	Get     *Operation `json:"get,omitempty"`
	Post    *Operation `json:"post,omitempty"`
	Put     *Operation `json:"put,omitempty"`
	Patch   *Operation `json:"patch,omitempty"`
	Delete  *Operation `json:"delete,omitempty"`
	Options *Operation `json:"options,omitempty"`
}

type RequestBody

type RequestBody struct {
	Description string               `json:"description,omitempty"`
	Required    bool                 `json:"required,omitempty"`
	Content     map[string]MediaType `json:"content"`
}

type Response

type Response struct {
	Description string               `json:"description"`
	Content     map[string]MediaType `json:"content,omitempty"`
}

type Schema

type Schema struct {
	Type                 string             `json:"type,omitempty"`
	Format               string             `json:"format,omitempty"`
	Description          string             `json:"description,omitempty"`
	Properties           map[string]*Schema `json:"properties,omitempty"`
	Items                *Schema            `json:"items,omitempty"`
	Required             []string           `json:"required,omitempty"`
	Ref                  string             `json:"$ref,omitempty"`
	Enum                 []interface{}      `json:"enum,omitempty"`
	Example              interface{}        `json:"example,omitempty"`
	AdditionalProperties interface{}        `json:"additionalProperties,omitempty"`
	OneOf                []*Schema          `json:"oneOf,omitempty"`
	AnyOf                []*Schema          `json:"anyOf,omitempty"`
}

type SecurityScheme

type SecurityScheme struct {
	Type         string `json:"type"` // "apiKey", "http", "oauth2", "openIdConnect"
	Description  string `json:"description,omitempty"`
	Name         string `json:"name,omitempty"`         // For apiKey
	In           string `json:"in,omitempty"`           // For apiKey: "query", "header", "cookie"
	Scheme       string `json:"scheme,omitempty"`       // For http: "basic", "bearer"
	BearerFormat string `json:"bearerFormat,omitempty"` // For http bearer
}

type Server

type Server struct {
	URL         string `json:"url"`
	Description string `json:"description,omitempty"`
}

type UIConfig added in v0.0.120

type UIConfig struct {
	// UIType specifies which UI framework to use (default: SwaggerUI)
	UIType UIType
	// SpecURL is the URL to the OpenAPI spec JSON (default: "/openapi")
	SpecURL string
	// Title is the page title (default: "API Documentation")
	Title string
	// FaviconURL is the URL to the favicon (optional)
	FaviconURL string
	// CustomCSS allows injecting custom CSS (optional)
	CustomCSS string
	// Theme for the UI (light/dark, depends on UI type)
	Theme string
}

UIConfig holds configuration for the OpenAPI UI handler

type UIType added in v0.0.120

type UIType string

UIType represents the type of OpenAPI UI to serve

const (
	// SwaggerUI is the most popular OpenAPI UI
	SwaggerUI UIType = "swagger-ui"
	// RapiDoc is a modern, customizable OpenAPI UI
	RapiDoc UIType = "rapidoc"
	// Redoc is a clean, responsive OpenAPI UI
	Redoc UIType = "redoc"
	// Scalar is a modern and sleek OpenAPI UI
	Scalar UIType = "scalar"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL