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.
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 identifier (e.g., “v1”, “v2”, “beta”)
Version-specific configuration (can be nil for defaults)
Returns:
error - Error if version already exists or validation fails
Behavior:
- Validates version string format
- Creates version-specific AST parser and schema generator
- Creates isolated API registry for the version
- Sets version-specific server URL in OpenAPI spec
- 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 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.
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
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:
- Direct spec loading from disk (if available)
- 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
- Version Naming: Use semantic versions (“v1”, “v2”) or descriptive names (“stable”, “beta”)
- Default Version: Always set a stable version as default
- Public Access: Only enable
PublicSwagger for stable, production-ready versions
- Route Isolation: Use
SetPrefix() to namespace each version’s routes
- Deprecation: Mark old versions with
Status: "deprecated" before removal
- Testing: Use
RegisterAllVersionRoutesForDocs() for build-time spec generation