Documentation
¶
Overview ¶
Package app provides a service bootstrap framework for building microservices.
The app package is the core of swlib, offering a fluent builder pattern for configuring and running microservices. It handles service lifecycle management, configuration parsing, database connections, gRPC/HTTP servers, service client management, and graceful shutdown.
Basic Usage ¶
Create a new application using the builder pattern:
app.New("myservice").
WithDefaultConfigFields(app.BackendServiceFields | app.DatabaseConnectionFields).
WithServiceClients(
app.NewServiceClient("authservice", authClientCtor),
).
WithDatabase(dbCtor, dbBootstrap).
WithGrpc(grpcConfig).
Run()
Configuration ¶
Configuration can be provided via environment variables or CLI flags. Pre-defined field groups simplify common configurations:
- BackendServiceFields: HTTP and gRPC port configuration
- DatabaseConnectionFields: PostgreSQL connection settings
- WebServiceFields: Static web server settings
- ClientCredentialsFields: OAuth client credentials
- HTMXServiceFields: HTMX-specific settings
Lifecycle ¶
The Run() method starts the application and blocks until an interrupt signal (SIGINT or SIGTERM) is received. It handles:
- Configuration parsing
- Database initialization
- Running initializers
- Starting background routines
- Starting gRPC and HTTP servers
- Graceful shutdown
Index ¶
- Variables
- func EnvServiceHost(serviceName string) string
- func EnvServicePort(serviceName string) string
- func GetAppData[T any](a App, key string) T
- func GetConfigField[T ConfigFieldValue](c *Config, name string) T
- func GetConfigFieldAsString(c *Config, name string) string
- func GetServiceClient[T grpcclients.Client](a App, name string) T
- func KeyServiceHost(serviceName string) string
- func KeyServicePort(serviceName string) string
- func ServiceClientHostAndPort(a App, serviceName string, tags ...string) func() (string, int)
- func SetAppData[T any](a App, key string, value T)
- type App
- type BackgroundRoutine
- type Callback
- type Config
- type ConfigField
- func NewBoolArrConfigField(name string, envVar string, description string, devaultValue []bool, ...) ConfigField
- func NewBoolConfigField(name string, envVar string, description string, defaultValue bool, ...) ConfigField
- func NewFloatArrConfigField(name string, envVar string, description string, devaultValue []float64, ...) ConfigField
- func NewFloatConfigField(name string, envVar string, description string, defaultValue float64, ...) ConfigField
- func NewIntArrConfigField(name string, envVar string, description string, devaultValue []int, ...) ConfigField
- func NewIntConfigField(name string, envVar string, description string, defaultValue int, ...) ConfigField
- func NewStringArrConfigField(name string, envVar string, description string, defaultValue []string, ...) ConfigField
- func NewStringConfigField(name string, envVar string, description string, defaultValue string, ...) ConfigField
- type ConfigFieldValue
- type DB
- type DBCtor
- type DefaultFlagGroup
- type FieldEnv
- type FieldKey
- type FlagGroupOverrides
- type ForwardResponseFn
- type GrpcConfig
- type GrpcInterceptor
- type GrpcServiceHooks
- type HeaderMathcerFn
- type HttpServer
- type ServiceClient
- type ServiceClientCtor
- type ServiceHTTPHandler
- type ServiceRegistrar
- type StartHttpServerFn
- type StopHttpServerFn
Constants ¶
This section is empty.
Variables ¶
var ( //DefConsulHosts = []string{"127.0.0.1:8500"} //DefConsulExternal = false //DefConsulInsecure = false DefHttpPort = 8080 DefFrontentHttpPort = 9000 DefGrpcPort = 8081 DefServerName = "http://localhost" DefPathPrefix = "" DefClientId = "" DefClientSecret = "" DefDBHost = "" DefDBPort = 0 DefDBName = "" DefDBUser = "" DefDBPassword = "" DefDBSSLMode = "disable" DefWebPort = 8000 DefWebPathPrefix = "/web" DefLogLevel = "info" )
Functions ¶
func EnvServiceHost ¶
EnvServiceHost returns the environment variable name for a service's host configuration. Example: EnvServiceHost("authservice") returns "AUTHSERVICE_HOST"
func EnvServicePort ¶
EnvServicePort returns the environment variable name for a service's port configuration. Example: EnvServicePort("authservice") returns "AUTHSERVICE_PORT"
func GetAppData ¶
GetAppData retrieves a typed value from the application's thread-safe data store. It uses Go generics to return the value with the correct type.
Example:
cm := app.GetAppData[*CacheManager](a, "cache-manager")
func GetConfigField ¶
func GetConfigField[T ConfigFieldValue](c *Config, name string) T
GetConfigField retrieves a typed configuration value by name. It uses Go generics to return the value with the correct type.
Example:
port := app.GetConfigField[int](cfg, "http-port") hosts := app.GetConfigField[flag.StringArr](cfg, "allowed-hosts")
func GetConfigFieldAsString ¶
GetConfigFieldAsString retrieves a configuration value as a string. It converts any value type to its string representation. Returns an empty string if the field does not exist or has no value.
func GetServiceClient ¶
func GetServiceClient[T grpcclients.Client](a App, name string) T
GetServiceClient retrieves a typed service client by name from the application. It uses Go generics to return the client with the correct type.
Example:
authClient := app.GetServiceClient[*authclient.Client](a, "authservice")
func KeyServiceHost ¶
KeyServiceHost returns the CLI flag name for a service's host configuration. Example: KeyServiceHost("authservice") returns "authservice-host"
func KeyServicePort ¶
KeyServicePort returns the CLI flag name for a service's port configuration. Example: KeyServicePort("authservice") returns "authservice-port"
func ServiceClientHostAndPort ¶
ServiceClientHostAndPort returns a function that retrieves the host and port for a service client. It first checks configuration values, then falls back to service discovery if available.
Example:
hostPort := app.ServiceClientHostAndPort(a, "authservice") host, port := hostPort() client, _ := authclient.New(host, port)
func SetAppData ¶
SetAppData stores a typed value in the application's thread-safe data store. This is a generic helper that provides type safety when storing data.
Example:
app.SetAppData(a, "cache-manager", cacheManager)
Types ¶
type App ¶
type App interface {
WithDefaultConfigFields(DefaultFlagGroup, FlagGroupOverrides) App
WithServiceClients(...*ServiceClient) App
WithConfigFields(...ConfigField) App
//WithBackendServices(...string) App
WithDatabase(DBCtor, Callback) App
WithAppData(string, any) App
WithInitializers(...Callback) App
WithBackgroundRoutines(...BackgroundRoutine) App
WithGrpc(*GrpcConfig) App
WithHTTP(StartHttpServerFn, StopHttpServerFn) App
Config() *Config
Logger() *log.Logger
Database() DB
//ServiceResolver() *svcreg.Resolver
BackgroundContext() context.Context
BackgroundWaitGroup() *sync.WaitGroup
ServiceClient(string) grpcclients.Client
SetAppData(string, any)
AppData(string) any
SetStaticHttpServer(HttpServer)
GetStaticHttpServer() HttpServer
Run()
}
App is the main application interface for configuring and running a microservice. It provides a fluent builder API for configuration and access to runtime components.
type BackgroundRoutine ¶
type BackgroundRoutine func(App)
BackgroundRoutine is a function type for long-running background tasks. Background routines run in separate goroutines and should respect the application's context for graceful shutdown.
Example:
func(a app.App) {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for {
select {
case <-a.BackgroundContext().Done():
a.BackgroundWaitGroup().Done()
return
case <-ticker.C:
// Perform periodic task
}
}
}
type Callback ¶
Callback is a function type used for initializers and bootstrap functions. It receives the App instance and returns an error if the operation fails.
type Config ¶
type Config struct {
// contains filtered or unexported fields
}
Config holds the application's configuration fields and provides methods for adding, retrieving, and parsing configuration values.
Configuration values can be set via:
- Environment variables (highest priority when parsing)
- CLI flags
- Default values (lowest priority)
func NewConfig ¶
func NewConfig( fields ...ConfigField, ) *Config
NewConfig creates a new Config instance with the given initial fields. Fields can also be added later using AddFields.
func (Config) AddFields ¶
func (c Config) AddFields(fields []ConfigField)
AddFields adds new configuration fields to the Config. Fields that already exist (by name) are not overwritten.
func (Config) Get ¶
func (c Config) Get(name string) ConfigField
Get retrieves a configuration field by name. Returns nil if the field does not exist.
func (Config) Parse ¶
Parse parses configuration from environment variables and CLI flags. It loads .env files using godotenv, then processes all registered fields.
The parsing order is:
- Load .env file if present
- Configure CLI flags with environment variable values as defaults
- Parse CLI flags (overrides environment variables)
- Set final values on all fields
type ConfigField ¶
type ConfigField interface {
// Name returns the CLI flag name (e.g., "http-port")
Name() string
// Env returns the environment variable name (e.g., "HTTP_PORT")
Env() string
// Description returns the human-readable description of the field
Description() string
// Value returns the current value of the field after parsing
Value() any
// contains filtered or unexported methods
}
ConfigField represents a single configuration field that can be set via environment variables or CLI flags. It provides access to the field's metadata and current value.
func NewBoolArrConfigField ¶
func NewBoolArrConfigField( name string, envVar string, description string, devaultValue []bool, flagset ...*flag.FlagSet, ) ConfigField
NewBoolArrConfigField creates a new boolean array configuration field. Values can be provided as comma-separated lists via environment variables or as multiple flag occurrences.
Example:
field := NewBoolArrConfigField("features", "FEATURES", "Feature flags", []bool{true, false})
func NewBoolConfigField ¶
func NewBoolConfigField( name string, envVar string, description string, defaultValue bool, flagset ...*flag.FlagSet, ) ConfigField
NewBoolConfigField creates a new boolean configuration field. Boolean values can be set via environment variables using "true", "false", "1", "0".
Example:
field := NewBoolConfigField("debug", "DEBUG", "Enable debug mode", false)
func NewFloatArrConfigField ¶
func NewFloatArrConfigField( name string, envVar string, description string, devaultValue []float64, flagset ...*flag.FlagSet, ) ConfigField
NewFloatArrConfigField creates a new float64 array configuration field. Values can be provided as comma-separated lists via environment variables or as multiple flag occurrences.
Example:
field := NewFloatArrConfigField("weights", "WEIGHTS", "Feature weights", []float64{0.5, 0.3, 0.2})
func NewFloatConfigField ¶
func NewFloatConfigField( name string, envVar string, description string, defaultValue float64, flagset ...*flag.FlagSet, ) ConfigField
NewFloatConfigField creates a new float64 configuration field.
Example:
field := NewFloatConfigField("timeout", "TIMEOUT", "Request timeout in seconds", 30.0)
func NewIntArrConfigField ¶
func NewIntArrConfigField( name string, envVar string, description string, devaultValue []int, flagset ...*flag.FlagSet, ) ConfigField
NewIntArrConfigField creates a new integer array configuration field. Values can be provided as comma-separated lists via environment variables or as multiple flag occurrences.
Example:
field := NewIntArrConfigField("ports", "PORTS", "Server ports", []int{8080, 8081})
func NewIntConfigField ¶
func NewIntConfigField( name string, envVar string, description string, defaultValue int, flagset ...*flag.FlagSet, ) ConfigField
NewIntConfigField creates a new integer configuration field.
Parameters:
- name: CLI flag name (e.g., "http-port")
- envVar: Environment variable name (e.g., "HTTP_PORT")
- description: Human-readable description for help text
- defaultValue: Default value if not set via env or flag
- flagset: Optional custom FlagSet (uses default if not provided)
Example:
field := NewIntConfigField("http-port", "HTTP_PORT", "HTTP server port", 8080)
func NewStringArrConfigField ¶
func NewStringArrConfigField( name string, envVar string, description string, defaultValue []string, flagset ...*flag.FlagSet, ) ConfigField
NewStringArrConfigField creates a new string array configuration field. Values can be provided as comma-separated lists via environment variables or as multiple flag occurrences.
Example:
field := NewStringArrConfigField("hosts", "HOSTS", "Allowed hosts", []string{"localhost"})
func NewStringConfigField ¶
func NewStringConfigField( name string, envVar string, description string, defaultValue string, flagset ...*flag.FlagSet, ) ConfigField
NewStringConfigField creates a new string configuration field.
Example:
field := NewStringConfigField("db-host", "DB_HOST", "Database hostname", "localhost")
type ConfigFieldValue ¶
type ConfigFieldValue interface {
string | int | float64 | bool | flg.StringArr | flg.IntArr | flg.FloatArr | flg.BoolArr
}
ConfigFieldValue is a type constraint for valid configuration field value types. Supported types include scalar types (string, int, float64, bool) and array types (StringArr, IntArr, FloatArr, BoolArr).
type DB ¶
DB is an interface for database connection wrappers. Implementations must provide access to the underlying *sql.DB connection.
type DBCtor ¶
DBCtor is a constructor function type for creating database connections. It receives the App instance for accessing configuration values.
Example:
func createDB(a app.App) app.DB {
connStr := fmt.Sprintf(
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
app.GetConfigField[string](a.Config(), app.KeyDBHost),
app.GetConfigField[int](a.Config(), app.KeyDBPort),
app.GetConfigField[string](a.Config(), app.KeyDBUser),
app.GetConfigField[string](a.Config(), app.KeyDBPassword),
app.GetConfigField[string](a.Config(), app.KeyDBName),
app.GetConfigField[string](a.Config(), app.KeyDBSSLMode),
)
db, err := sql.Open("postgres", connStr)
// ... error handling and wrapper creation
return &MyDBWrapper{db: db}
}
type DefaultFlagGroup ¶
type DefaultFlagGroup uint16
DefaultFlagGroup represents a bitmask for selecting pre-defined configuration field groups. Multiple groups can be combined using bitwise OR.
Example:
app.New("myservice").
WithDefaultConfigFields(
app.BackendServiceFields | app.DatabaseConnectionFields,
nil,
)
const ( // NoDefaultFields adds no default configuration fields NoDefaultFields DefaultFlagGroup = 0x0000 // BackendServiceFields adds HTTP and gRPC port configuration and logger configuration // Fields: http-port (HTTP_PORT), grpc-port (GRPC_PORT), log-level (LOG_LEVEL) BackendServiceFields DefaultFlagGroup = 0x0002 | 0x0080 // HTMXServiceFields adds configuration for HTMX-based web services // Fields: http-port (HTTP_PORT), server-name (SERVER_NAME), path-prefix (PATH_PREFIX) HTMXServiceFields DefaultFlagGroup = 0x0004 // ClientCredentialsFields adds OAuth2 client credentials configuration // Fields: client-id (CLIENT_ID), client-secret (CLIENT_SECRET) ClientCredentialsFields DefaultFlagGroup = 0x0008 // DatabaseConnectionFields adds PostgreSQL database connection configuration // Fields: db-host, db-port, db-name, db-user, db-password, db-ssl-mode DatabaseConnectionFields DefaultFlagGroup = 0x0010 // WebServiceFields adds static web server configuration // Fields: web-port (WEB_PORT), web-path-prefix (WEB_PATH_PREFIX) WebServiceFields DefaultFlagGroup = 0x0040 // LoggerFields adds logger configuration // Fields: log-level (LOG_LEVEL) LoggerFields DefaultFlagGroup = 0x0080 )
Pre-defined configuration field groups for common service configurations. These can be combined using bitwise OR when calling WithDefaultConfigFields.
type FieldEnv ¶
type FieldEnv = string
FieldEnv is a type alias for environment variable names (e.g., "HTTP_PORT").
const ( //EnvConsulHosts FieldEnv = "CONSUL_HOSTS" //EnvConsulExternal FieldEnv = "CONSUL_EXTERNAL" //EnvConsulInsecure FieldEnv = "CONSUL_INSECURE" EnvHttpPort FieldEnv = "HTTP_PORT" EnvGrpcPort FieldEnv = "GRPC_PORT" EnvServerName FieldEnv = "SERVER_NAME" EnvPathPrefix FieldEnv = "PATH_PREFIX" EnvClientId FieldEnv = "CLIENT_ID" EnvClientSecret FieldEnv = "CLIENT_SECRET" EnvDBHost FieldEnv = "DB_HOST" EnvDBPort FieldEnv = "DB_PORT" EnvDBName FieldEnv = "DB_NAME" EnvDBUser FieldEnv = "DB_USER" EnvDBPassword FieldEnv = "DB_PASSWORD" EnvDBSSLMode FieldEnv = "DB_SSL_MODE" EnvWebPort FieldEnv = "WEB_PORT" EnvWebPathPrefix FieldEnv = "WEB_PATH_PREFIX" EnvLogLevel FieldEnv = "LOG_LEVEL" )
type FieldKey ¶
type FieldKey = string
FieldKey is a type alias for CLI flag names (e.g., "http-port").
const ( //KeyConsulHosts FieldKey = "consul-hosts" //KeyConsulExternal FieldKey = "consul-external" //KeyConsulInsecure FieldKey = "consul-insecure" KeyHttpPort FieldKey = "http-port" KeyGrpcPort FieldKey = "grpc-port" KeyServerName FieldKey = "server-name" KeyPathPrefix FieldKey = "path-prefix" KeyClientId FieldKey = "client-id" KeyClientSecret FieldKey = "client-secret" KeyDBHost FieldKey = "db-host" KeyDBPort FieldKey = "db-port" KeyDBName FieldKey = "db-name" KeyDBUser FieldKey = "db-user" KeyDBPassword FieldKey = "db-password" KeyDBSSLMode FieldKey = "db-ssl-mode" KeyWebPort FieldKey = "web-port" KeyWebPathPrefix FieldKey = "web-path-prefix" KeyLogLevel FieldKey = "log-level" )
Configuration field keys for CLI flags. These constants define the flag names used when configuring the application.
type FlagGroupOverrides ¶
FlagGroupOverrides allows overriding default values for pre-defined field groups. Keys are field names (e.g., KeyHttpPort), values are the new defaults.
type ForwardResponseFn ¶
ForwardResponseFn is a callback for modifying HTTP responses in the gateway.
type GrpcConfig ¶
type GrpcConfig struct {
// Interceptors specifies which gRPC interceptors to enable
Interceptors GrpcInterceptor
// JWTPublicKeysFn provides public keys for JWT validation (required if AuthInterceptor enabled)
JWTPublicKeysFn security.PublicKeysFn
// ServiceRegistrars contains the gRPC service registration hooks
ServiceRegistrars []GrpcServiceHooks
// ForwardResponseFn is an optional callback for modifying HTTP responses
ForwardResponseFn ForwardResponseFn
// HeaderMatcherFn is an optional callback for customizing header mapping
HeaderMatcherFn HeaderMathcerFn
}
GrpcConfig holds the configuration for the gRPC server and HTTP gateway.
func NewGrpcConfig ¶
func NewGrpcConfig( interceptors GrpcInterceptor, jwtPublicKeysFn security.PublicKeysFn, serviceRegistrars ...GrpcServiceHooks, ) *GrpcConfig
NewGrpcConfig creates a new GrpcConfig with the specified interceptors, JWT public keys function, and service registrars.
Example:
grpcConfig := app.NewGrpcConfig(
app.AuthInterceptor | app.ClientInfoInterceptor,
getPublicKeys,
app.GrpcServiceHooks{
ServiceRegistrar: registerMyService,
ServiceHTTPHandler: pb.RegisterMyServiceHandlerFromEndpoint,
},
)
func (*GrpcConfig) SetForwardResponseFn ¶
func (cfg *GrpcConfig) SetForwardResponseFn(fn ForwardResponseFn)
func (*GrpcConfig) SetHeaderMatcherFn ¶
func (cfg *GrpcConfig) SetHeaderMatcherFn(fn HeaderMathcerFn)
type GrpcInterceptor ¶
type GrpcInterceptor = uint16
GrpcInterceptor is a bitmask type for selecting gRPC server interceptors. Multiple interceptors can be combined using bitwise OR.
const ( // NoInterceptor disables all interceptors NoInterceptor GrpcInterceptor = 0x0000 // AuthInterceptor enables JWT authentication for incoming requests AuthInterceptor GrpcInterceptor = 0x0001 // ClientInfoInterceptor extracts client information (IP, user agent) from requests ClientInfoInterceptor GrpcInterceptor = 0x0002 )
gRPC interceptor options for server-side middleware.
type GrpcServiceHooks ¶
type GrpcServiceHooks struct {
ServiceRegistrar ServiceRegistrar
ServiceHTTPHandler ServiceHTTPHandler
}
GrpcServiceHooks combines the gRPC service registration and HTTP handler setup.
type HeaderMathcerFn ¶
type HeaderMathcerFn = runtime.HeaderMatcherFunc
HeaderMathcerFn is used to customize HTTP header to gRPC metadata mapping.
type HttpServer ¶
HttpServer is an interface for HTTP server implementations that can be registered with the application for lifecycle management.
type ServiceClient ¶
type ServiceClient struct {
Name string
Ctor ServiceClientCtor
// contains filtered or unexported fields
}
ServiceClient holds the configuration and instance of a gRPC service client. It manages the lifecycle of inter-service connections.
func NewServiceClient ¶
func NewServiceClient( name string, Ctor ServiceClientCtor, ) *ServiceClient
NewServiceClient creates a new ServiceClient configuration. The constructor function will be called during application initialization.
Example:
app.NewServiceClient("authservice", func(a app.App) grpcclients.Client {
hostPort := app.ServiceClientHostAndPort(a, "authservice")
host, port := hostPort()
client, _ := authclient.New(host, port)
return client
})
type ServiceClientCtor ¶
type ServiceClientCtor func(App) grpcclients.Client
ServiceClientCtor is a constructor function type for creating gRPC service clients. It receives the App instance for accessing configuration values (host/port).
type ServiceHTTPHandler ¶
ServiceHTTPHandler is a function type for registering HTTP gateway handlers. It's used to expose gRPC services via REST endpoints using grpc-gateway.
type ServiceRegistrar ¶
type ServiceRegistrar func(grpc.ServiceRegistrar, App)
ServiceRegistrar is a function type for registering gRPC services with the server. It receives the gRPC ServiceRegistrar and the App instance.
type StartHttpServerFn ¶
StartHttpServerFn is a function type for starting a custom HTTP server. It receives the App instance and should return an error if startup fails. The function should start the server asynchronously (in a goroutine).
type StopHttpServerFn ¶
type StopHttpServerFn func(App)
StopHttpServerFn is a function type for stopping a custom HTTP server. It receives the App instance and should perform graceful shutdown.