lokstra_registry
Simplified API for the Lokstra registry system
Overview
The lokstra_registry package provides a clean, package-level API for registering and accessing services, middleware, routers, and configuration. It wraps deploy.GlobalRegistry() to provide:
- ✅ Shorter import path
- ✅ Package-level functions (no singleton access)
- ✅ Generic helper functions like
GetService[T] - ✅ Cleaner developer experience
Import Path
import "github.com/primadi/lokstra/lokstra_registry"
Service Registration
RegisterServiceType
Registers a service factory with optional metadata for auto-router generation.
Signature:
func RegisterServiceType(
serviceType string,
local, remote any,
options ...deploy.RegisterServiceTypeOption,
)
Parameters:
serviceType- Unique identifier for the service typelocal- Local factory function (for in-process services)remote- Remote factory function (for external API wrappers), orniloptions- Optional metadata (resource name, convention, dependencies, etc.)
Factory Signatures (All Auto-Wrapped):
// Simplest - no dependencies, no config
func() any
// With config
func(cfg map[string]any) any
// Full control - with dependencies and config
func(deps, cfg map[string]any) any
Example:
// Simple factory (no deps, no config)
lokstra_registry.RegisterServiceType("user-service",
func() any {
return &UserService{}
},
nil, // No remote implementation
deploy.WithResource("user", "users"),
)
// With config
lokstra_registry.RegisterServiceType("db-service",
func(cfg map[string]any) any {
dsn := cfg["dsn"].(string)
return db.NewConnection(dsn)
},
nil,
)
// Full signature with deps
lokstra_registry.RegisterServiceType("order-service",
func(deps, cfg map[string]any) any {
userSvc := deps["userService"].(*service.Cached[*UserService])
maxItems := cfg["max_items"].(int)
return &OrderService{
userService: userSvc,
maxItems: maxItems,
}
},
nil,
deploy.WithDependencies("userService"),
)
// With remote implementation
lokstra_registry.RegisterServiceType("payment-service",
// Local implementation
func(deps, cfg map[string]any) any {
return &PaymentService{db: deps["db"].(*DBService)}
},
// Remote implementation (API client)
func(deps, cfg map[string]any) any {
proxy := deps["remote"].(*proxy.Service)
return &PaymentServiceRemote{proxy: proxy}
},
deploy.WithResource("payment", "payments"),
deploy.WithDependencies("db"),
)
Options:
deploy.WithResource(singular, plural string)
deploy.WithConvention(convention string)
deploy.WithDependencies(deps ...string)
deploy.WithPathPrefix(prefix string)
deploy.WithMiddleware(names ...string)
deploy.WithRouteOverride(methodName, path string)
deploy.WithHiddenMethods(methods ...string)
DefineService
Defines a service instance in the global registry (code-based config).
Signature:
func DefineService(def *schema.ServiceDef)
Example:
lokstra_registry.DefineService(&schema.ServiceDef{
Name: "user-service",
Type: "user-service-factory",
Config: map[string]any{
"max_connections": 10,
},
DependsOn: []string{"db-service"},
})
Use Cases:
- Code-based service configuration
- Dynamic service definitions
- Testing scenarios
RegisterLazyService
Registers a lazy service that will be instantiated on first access.
Signature:
func RegisterLazyService(name string, factory any, config map[string]any)
Factory Signatures:
func() any // No params (simplest!)
func(cfg map[string]any) any // With config
Benefits:
- ✅ No creation order requirements
- ✅ Dependencies resolved automatically
- ✅ Thread-safe singleton pattern
- ✅ Services only created when needed
Example:
// With config
lokstra_registry.RegisterLazyService("db-main", func(cfg map[string]any) any {
dsn := cfg["dsn"].(string)
return db.NewConnection(dsn)
}, map[string]any{
"dsn": "postgresql://localhost/main",
})
// Without params (resolve deps manually)
lokstra_registry.RegisterLazyService("user-repo", func() any {
db := lokstra_registry.MustGetService[*DB]("db-main")
return repository.NewUserRepository(db)
}, nil)
RegisterLazyServiceWithDeps
Registers a lazy service with explicit dependency injection.
Signature:
func RegisterLazyServiceWithDeps(
name string,
factory any,
deps map[string]string,
config map[string]any,
opts ...deploy.LazyServiceOption,
)
Parameters:
name- Service namefactory- Factory functionfunc(deps, cfg map[string]any) anydeps- Dependency mapping:map[factoryKey]serviceNameconfig- Service configurationopts- Optional registration mode
Example:
lokstra_registry.RegisterLazyServiceWithDeps("order-service",
func(deps, cfg map[string]any) any {
// deps already contains resolved services!
userSvc := deps["userService"].(*UserService)
orderRepo := deps["orderRepo"].(*OrderRepository)
maxItems := cfg["max_items"].(int)
return &OrderService{
userService: userSvc,
orderRepo: orderRepo,
maxItems: maxItems,
}
},
map[string]string{
"userService": "user-service",
"orderRepo": "order-repo",
},
map[string]any{"max_items": 5},
)
// Skip if already registered
lokstra_registry.RegisterLazyServiceWithDeps(name, factory, deps, cfg,
deploy.WithRegistrationMode(deploy.LazyServiceSkip))
// Override existing registration
lokstra_registry.RegisterLazyServiceWithDeps(name, factory, deps, cfg,
deploy.WithRegistrationMode(deploy.LazyServiceOverride))
Service Access
GetService
Retrieves a service with type assertion (generic).
Signature:
func GetService[T any](name string) T
Returns:
- Service instance of type
T, or zero value if not found
Example:
userSvc := lokstra_registry.GetService[*UserService]("user-service")
if userSvc != nil {
users := userSvc.GetAll()
}
MustGetService
Retrieves a service with type assertion (panics if not found).
Signature:
func MustGetService[T any](name string) T
Returns:
- Service instance of type
T
Panics:
- If service not found
- If type mismatch
Example:
userSvc := lokstra_registry.MustGetService[*UserService]("user-service")
users := userSvc.GetAll() // Safe to use directly
When to Use:
- Required dependencies
- Fail-fast initialization
- Clear error reporting
TryGetService
Retrieves a service with type assertion (safe version).
Signature:
func TryGetService[T any](name string) (T, bool)
Returns:
(value, true)if found and type matches(zero, false)otherwise
Example:
if userSvc, ok := lokstra_registry.TryGetService[*UserService]("user-service"); ok {
users := userSvc.GetAll()
} else {
log.Println("User service not available")
}
GetServiceAny
Retrieves a service without type assertion (non-generic).
Signature:
func GetServiceAny(name string) (any, bool)
Returns:
(instance, true)if found(nil, false)if not found
Example:
instance, ok := lokstra_registry.GetServiceAny("user-service")
if ok {
userSvc := instance.(*UserService)
users := userSvc.GetAll()
}
GetLazyService
Creates a lazy-loading service wrapper.
Signature:
func GetLazyService[T any](serviceName string) *service.Cached[T]
Returns:
*service.Cached[T]- Lazy service loader
Example:
// In handler setup
type UserHandler struct {
userService *service.Cached[*UserService]
}
func NewUserHandler() *UserHandler {
return &UserHandler{
userService: lokstra_registry.GetLazyService[*UserService]("user-service"),
}
}
// In handler - service loaded only when first accessed
func (h *UserHandler) GetUsers(ctx *request.Context) error {
users := h.userService.Get().GetAll() // Lazy loaded here!
return ctx.Api.Ok(users)
}
RegisterService
Registers a pre-instantiated service instance.
Signature:
func RegisterService(name string, instance any)
Example:
userSvc := &UserService{}
lokstra_registry.RegisterService("user-service", userSvc)
Use Cases:
- Manual service registration
- Testing with mocks
- Pre-initialized services
GetServiceFactory
Returns the service factory for a service type.
Signature:
func GetServiceFactory(serviceType string, isLocal bool) deploy.ServiceFactory
Parameters:
serviceType- Service type nameisLocal-truefor local factory,falsefor remote
Returns:
- Factory function
Middleware Registration
RegisterMiddlewareFactory
Registers a middleware factory function.
Signature:
func RegisterMiddlewareFactory(
mwType string,
factory any,
opts ...RegisterOption,
)
Factory Signatures:
func(config map[string]any) any
func(config map[string]any) request.HandlerFunc // Old pattern
Example:
lokstra_registry.RegisterMiddlewareFactory("logger", func(cfg map[string]any) any {
level := cfg["level"].(string)
return func(ctx *request.Context) error {
log.Printf("[%s] %s %s", level, ctx.R.Method, ctx.R.URL.Path)
return ctx.Next()
}
})
// Allow override
lokstra_registry.RegisterMiddlewareFactory("logger", newLoggerFactory,
lokstra_registry.AllowOverride(true))
RegisterMiddlewareName
Registers a named middleware instance with config.
Signature:
func RegisterMiddlewareName(
mwName string,
mwType string,
config map[string]any,
opts ...RegisterOption,
)
Example:
// Register factory
lokstra_registry.RegisterMiddlewareFactory("logger", loggerFactory)
// Register named instances with different configs
lokstra_registry.RegisterMiddlewareName("logger-debug", "logger",
map[string]any{"level": "debug"})
lokstra_registry.RegisterMiddlewareName("logger-info", "logger",
map[string]any{"level": "info"})
RegisterMiddleware
Registers a pre-instantiated middleware.
Signature:
func RegisterMiddleware(name string, handler request.HandlerFunc)
Example:
logger := func(ctx *request.Context) error {
log.Printf("%s %s", ctx.R.Method, ctx.R.URL.Path)
return ctx.Next()
}
lokstra_registry.RegisterMiddleware("logger", logger)
GetMiddleware
Retrieves a middleware instance.
Signature:
func GetMiddleware(name string) (request.HandlerFunc, bool)
Returns:
(handler, true)if found(nil, false)if not found
CreateMiddleware
Creates a middleware from its definition and caches it.
Signature:
func CreateMiddleware(name string) request.HandlerFunc
Example:
logger := lokstra_registry.CreateMiddleware("logger-debug")
router.Use(logger)
Router Registration
RegisterRouter
Registers a router instance.
Signature:
func RegisterRouter(name string, r router.Router)
Example:
userRouter := lokstra.NewRouter()
userRouter.GET("/users", handlers.GetUsers)
lokstra_registry.RegisterRouter("user-router", userRouter)
GetRouter
Retrieves a router instance.
Signature:
func GetRouter(name string) router.Router
Returns:
- Router instance, or
nilif not found
GetAllRouters
Returns all registered routers.
Signature:
func GetAllRouters() map[string]router.Router
Returns:
- Map of router name to router instance
Configuration
DefineConfig
Defines a configuration value.
Signature:
func DefineConfig(name string, value any)
Example:
lokstra_registry.DefineConfig("db.dsn", "postgresql://localhost/mydb")
lokstra_registry.DefineConfig("app.max_connections", 100)
GetResolvedConfig
Gets a resolved configuration value.
Signature:
func GetResolvedConfig(key string) (any, bool)
Returns:
(value, true)if found(nil, false)if not found
GetConfig
Retrieves a configuration value with type assertion and default value.
Signature:
func GetConfig[T any](name string, defaultValue T) T
Parameters:
name- Configuration key (e.g., “db.dsn”, “app.max_connections”)defaultValue- Default value if configuration not found
Returns:
- Config value of type
T, ordefaultValueif not found
Type Conversion:
The function attempts to convert the stored value to type T:
- String → String (direct)
- Number → Number (with type conversion)
- Bool → Bool (direct)
- Object → Struct (via JSON unmarshaling)
- Array → Slice (via JSON unmarshaling)
Example:
// Simple types
dsn := lokstra_registry.GetConfig("db.dsn", "postgresql://localhost/default")
maxConn := lokstra_registry.GetConfig("app.max_connections", 10)
debug := lokstra_registry.GetConfig("app.debug", false)
// Complex types
type Features struct {
EnableLogging bool `json:"enable_logging"`
EnableMetrics bool `json:"enable_metrics"`
MaxRetries int `json:"max_retries"`
}
features := lokstra_registry.GetConfig("app.features", Features{
EnableLogging: true,
EnableMetrics: false,
MaxRetries: 3,
})
YAML Configuration:
configs:
- name: db.dsn
value: "${DATABASE_URL:postgresql://localhost/mydb}"
- name: app.max_connections
value: 25
- name: app.debug
value: true
- name: app.features
value:
enable_logging: true
enable_metrics: true
max_retries: 5
Accessing in Service Factory:
func UserServiceFactory(deps map[string]any, config map[string]any) any {
// Get from service-specific config
maxItems := 100
if val, ok := config["max_items"].(int); ok {
maxItems = val
}
// Or get from global config registry
timeout := lokstra_registry.GetConfig("app.timeout", 30)
return &UserServiceImpl{
MaxItems: maxItems,
Timeout: time.Duration(timeout) * time.Second,
}
}
Best Practices:
- ✅ Use specific config keys:
db.dsn, not justdsn - ✅ Always provide sensible defaults
- ✅ Use
GetConfigin factory functions, not in handlers - ✅ Cache config values in service structs for performance
- 🚫 Don’t call
GetConfigin hot paths (per-request)
Related:
- See Configuration System for YAML configuration
- See Variable Resolvers for dynamic config sources
Shutdown Management
ShutdownServices
Gracefully shuts down all services implementing Shutdownable.
Signature:
func ShutdownServices()
Shutdownable Interface:
type Shutdownable interface {
Shutdown() error
}
Example Service:
type DatabaseService struct {
conn *sql.DB
}
func (s *DatabaseService) Shutdown() error {
log.Println("Closing database connection")
return s.conn.Close()
}
Usage in main.go:
func main() {
// Register services
lokstra_registry.RegisterService("db", dbService)
// Setup graceful shutdown
defer lokstra_registry.ShutdownServices()
// Or with signal handling
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
log.Println("Shutting down...")
lokstra_registry.ShutdownServices()
os.Exit(0)
}()
// Start server
if err := server.Run(30 * time.Second); err != nil {
fmt.Println("Error starting server:", err)
}
}
Advanced Functions
Global
Returns the underlying global registry instance.
Signature:
func Global() *deploy.GlobalRegistry
Returns:
- Global registry instance
Example:
registry := lokstra_registry.Global()
// Access low-level registry APIs
Complete Examples
Service Registration Pattern
package main
import (
"github.com/primadi/lokstra"
"github.com/primadi/lokstra/core/deploy"
"github.com/primadi/lokstra/lokstra_registry"
)
func main() {
// Register service types
lokstra_registry.RegisterServiceType("user-service",
func(deps, cfg map[string]any) any {
db := deps["db"].(*service.Cached[*DBService])
return &UserService{db: db}
},
nil,
deploy.WithResource("user", "users"),
deploy.WithDependencies("db"),
)
lokstra_registry.RegisterServiceType("db-service",
func(cfg map[string]any) any {
dsn := cfg["dsn"].(string)
return db.Connect(dsn)
},
nil,
)
// Define service instances
lokstra_registry.DefineService(&schema.ServiceDef{
Name: "db",
Type: "db-service",
Config: map[string]any{"dsn": "postgresql://localhost/mydb"},
})
lokstra_registry.DefineService(&schema.ServiceDef{
Name: "user-svc",
Type: "user-service",
DependsOn: []string{"db"},
})
// Access services
userSvc := lokstra_registry.MustGetService[*UserService]("user-svc")
users := userSvc.GetAll()
}
Middleware Registration Pattern
func main() {
// Register middleware factory
lokstra_registry.RegisterMiddlewareFactory("logger", func(cfg map[string]any) any {
level := cfg["level"].(string)
return func(ctx *request.Context) error {
log.Printf("[%s] %s %s", level, ctx.R.Method, ctx.R.URL.Path)
return ctx.Next()
}
})
// Register named instances
lokstra_registry.RegisterMiddlewareName("logger-debug", "logger",
map[string]any{"level": "DEBUG"})
lokstra_registry.RegisterMiddlewareName("logger-info", "logger",
map[string]any{"level": "INFO"})
// Use in router
router := lokstra.NewRouter()
router.Use(lokstra_registry.CreateMiddleware("logger-info"))
router.GET("/users", handlers.GetUsers)
}
Lazy Service Pattern
func main() {
// Register lazy services (no order required!)
lokstra_registry.RegisterLazyService("db", func(cfg map[string]any) any {
return db.Connect(cfg["dsn"].(string))
}, map[string]any{"dsn": "postgresql://localhost/mydb"})
lokstra_registry.RegisterLazyService("user-repo", func() any {
db := lokstra_registry.MustGetService[*DB]("db")
return repository.NewUserRepository(db)
}, nil)
lokstra_registry.RegisterLazyService("user-service", func() any {
repo := lokstra_registry.MustGetService[*UserRepo]("user-repo")
return service.NewUserService(repo)
}, nil)
// Services created on first access
userSvc := lokstra_registry.MustGetService[*UserService]("user-service")
}
See Also
- Service - Lazy service loading
- Service Registration - Detailed registration patterns
- Middleware Registration - Middleware patterns
- Router Registration - Router factories
- Deploy - Deployment configuration
Related Guides
- Service Essentials - Service basics
- Dependency Injection - Advanced DI patterns
- Testing - Testing strategies