Skip to main content

Overview

The APIVersionManager orchestrates versioned API documentation by maintaining isolated registries, parsers, and configurations for each API version. It enables side-by-side deployment of multiple API versions with automatic OpenAPI spec generation and Swagger UI support.

Type Definition

type APIVersionManager struct {
    mu              sync.RWMutex
    versions        []string                             // ordered list of versions
    defaultVersion  string                               // default version to use
    registries      map[string]*APIRegistry              // separate registry per version
    configs         map[string]*APIDocsConfig            // version-specific configs
    routeRegistrars map[string]func(*VersionedAPIRouter) // per-version route registration callbacks
    createdAt       time.Time                            // when manager was created
    lastModified    time.Time                            // last time versions were modified
}
Each API version gets its own isolated ASTParser, SchemaGenerator, and APIRegistry. This ensures complete separation between versions with independent component schemas and route registrations.

Constructor Functions

NewAPIVersionManager

func NewAPIVersionManager() *APIVersionManager
Creates a new version manager with empty state. Returns:
  • *APIVersionManager - New manager instance
Example:
vm := api.NewAPIVersionManager()

NewAPIVersionManagerWithDefault

func NewAPIVersionManagerWithDefault(defaultVersion string) *APIVersionManager
Creates a version manager with a pre-set default version.
defaultVersion
string
required
Version identifier to use as default (e.g., “v1”, “v2”)
Example:
vm := api.NewAPIVersionManagerWithDefault("v1")

Version Management

RegisterVersion

func (vm *APIVersionManager) RegisterVersion(version string, config *APIDocsConfig) error
Registers a new API version with its own registry and configuration.
version
string
required
Version identifier (e.g., “v1”, “v2”, “beta”)
config
*APIDocsConfig
Version-specific configuration (can be nil for defaults)
Returns:
  • error - Error if version already exists or validation fails
Behavior:
  1. Validates version string format
  2. Creates version-specific AST parser and schema generator
  3. Creates isolated API registry for the version
  4. Sets version-specific server URL in OpenAPI spec
  5. Automatically becomes default version if first registered
Example:
config := &api.APIDocsConfig{
    Title:   "My API v1",
    Version: "v1",
    BaseURL: "https://api.example.com",
    Status:  "stable",
}
err := vm.RegisterVersion("v1", config)

RemoveVersion

func (vm *APIVersionManager) RemoveVersion(version string) error
Removes a version and its registry. Cannot remove the default version.
version
string
required
Version identifier to remove

GetVersionConfig

func (vm *APIVersionManager) GetVersionConfig(version string) (*APIDocsConfig, error)
Retrieves the configuration for a specific version. Location: core/server/api/version_manager.go:198

GetVersionRegistry

func (vm *APIVersionManager) GetVersionRegistry(version string) (*APIRegistry, error)
Retrieves the API registry for a specific version. Location: core/server/api/version_manager.go:209

Route Registration

SetVersionRouteRegistrar

func (vm *APIVersionManager) SetVersionRouteRegistrar(
    version string,
    registrar func(*VersionedAPIRouter),
) error
Sets the route registration callback for a version.
version
string
required
Version identifier
registrar
func(*VersionedAPIRouter)
required
Function that registers routes for this version
Example:
err := vm.SetVersionRouteRegistrar("v1", func(router *api.VersionedAPIRouter) {
    router.GET("/users", listUsersHandler)
    router.POST("/users", createUserHandler)
})

RegisterAllVersionRoutes

func (vm *APIVersionManager) RegisterAllVersionRoutes(e *core.ServeEvent) error
Registers all version routes to a ServeEvent router and docs registries. Location: core/server/api/version_manager.go:300

RegisterAllVersionRoutesForDocs

func (vm *APIVersionManager) RegisterAllVersionRoutesForDocs() error
Registers all version routes only to docs registries (for build-time spec generation). Location: core/server/api/version_manager.go:315

Version Information

GetDefaultVersion

func (vm *APIVersionManager) GetDefaultVersion() string
Returns the default version identifier.

SetDefaultVersion

func (vm *APIVersionManager) SetDefaultVersion(version string) error
Sets the default API version. Version must exist.

GetAllVersions

func (vm *APIVersionManager) GetAllVersions() []string
Returns all registered versions (sorted). Returns a copy to prevent external modifications.

GetVersionInfo

func (vm *APIVersionManager) GetVersionInfo(version string) (*VersionInfo, error)
Returns detailed information about a specific version. Returns:
type VersionInfo struct {
    Version   string         `json:"version"`
    Status    string         `json:"status"`      // "stable", "development", "deprecated"
    CreatedAt time.Time      `json:"created_at"`
    UpdatedAt time.Time      `json:"updated_at"`
    Config    *APIDocsConfig `json:"config"`
    Stats     map[string]int `json:"stats"`
    Endpoints int            `json:"endpoints"`
}
Location: core/server/api/version_manager.go:650

HTTP Handlers

RegisterWithServer

func (vm *APIVersionManager) RegisterWithServer(app core.App)
Registers version management endpoints with the PocketBase app:
  • GET /api/docs/versions - List all versions (requires auth)
  • GET /api/docs/debug/ast - AST pipeline introspection (requires auth)
  • GET /api/docs/{version} - Version-specific OpenAPI spec (requires auth)
  • GET /api/docs/{version}/spec - Public OpenAPI spec (if PublicSwagger enabled)
  • GET /api/docs/{version}/swagger - Swagger UI (if PublicSwagger enabled)
  • GET /api/{version}/schema/config - Schema configuration (requires auth)
Location: core/server/api/version_manager.go:706

VersionsHandler

func (vm *APIVersionManager) VersionsHandler(c *core.RequestEvent) error
HTTP handler that returns list of all available API versions. Response:
{
    "versions": [...],
    "default_version": "v1",
    "total_versions": 2,
    "generated_at": "2024-03-04T12:00:00Z"
}
Location: core/server/api/version_manager.go:756

GetVersionOpenAPI

func (vm *APIVersionManager) GetVersionOpenAPI(
    c *core.RequestEvent,
    version string,
) error
Returns the complete OpenAPI schema for a specific version. Priority:
  1. Direct spec loading from disk (if available)
  2. Fallback to runtime registry generation
Location: core/server/api/version_manager.go:827

ServeSwaggerUI

func (vm *APIVersionManager) ServeSwaggerUI(
    c *core.RequestEvent,
    version string,
) error
Serves the Swagger UI HTML page for the given API version with dark mode support. Location: core/server/api/version_manager.go:898
Uses SwaggerDark theme by Amoenus (MIT License) for automatic dark mode support based on user’s system preferences.

Complete Example

package main

import (
    "github.com/magooney-loon/pb-ext/core/server/api"
    "github.com/pocketbase/pocketbase/core"
)

func setupVersionedAPI(app core.App) (*api.APIVersionManager, error) {
    // Create version manager
    vm := api.NewAPIVersionManagerWithDefault("v1")
    
    // Register v1
    v1Config := &api.APIDocsConfig{
        Title:         "My API v1",
        Version:       "v1",
        BaseURL:       "https://api.example.com",
        Status:        "stable",
        PublicSwagger: true,
    }
    if err := vm.RegisterVersion("v1", v1Config); err != nil {
        return nil, err
    }
    
    // Register v2 (beta)
    v2Config := &api.APIDocsConfig{
        Title:         "My API v2 (Beta)",
        Version:       "v2",
        BaseURL:       "https://api.example.com",
        Status:        "development",
        PublicSwagger: false,
    }
    if err := vm.RegisterVersion("v2", v2Config); err != nil {
        return nil, err
    }
    
    // Register routes for each version
    if err := vm.SetVersionRouteRegistrar("v1", registerV1Routes); err != nil {
        return nil, err
    }
    if err := vm.SetVersionRouteRegistrar("v2", registerV2Routes); err != nil {
        return nil, err
    }
    
    // Register HTTP endpoints
    vm.RegisterWithServer(app)
    
    return vm, nil
}

func registerV1Routes(router *api.VersionedAPIRouter) {
    api := router.SetPrefix("/api/v1")
    
    api.GET("/users", listUsersHandler)
    api.POST("/users", createUserHandler)
    api.GET("/users/{id}", getUserHandler)
}

func registerV2Routes(router *api.VersionedAPIRouter) {
    api := router.SetPrefix("/api/v2")
    
    // v2 has additional endpoints
    api.GET("/users", listUsersV2Handler)
    api.POST("/users", createUserV2Handler)
    api.GET("/users/{id}", getUserV2Handler)
    api.PATCH("/users/{id}", patchUserHandler) // New in v2
}

Best Practices

  1. Version Naming: Use semantic versions (“v1”, “v2”) or descriptive names (“stable”, “beta”)
  2. Default Version: Always set a stable version as default
  3. Public Access: Only enable PublicSwagger for stable, production-ready versions
  4. Route Isolation: Use SetPrefix() to namespace each version’s routes
  5. Deprecation: Mark old versions with Status: "deprecated" before removal
  6. Testing: Use RegisterAllVersionRoutesForDocs() for build-time spec generation