app

package
v0.1.4 Latest Latest
Warning

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

Go to latest
Published: Jun 2, 2026 License: AGPL-3.0 Imports: 25 Imported by: 0

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

Constants

This section is empty.

Variables

View Source
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

func EnvServiceHost(serviceName string) string

EnvServiceHost returns the environment variable name for a service's host configuration. Example: EnvServiceHost("authservice") returns "AUTHSERVICE_HOST"

func EnvServicePort

func EnvServicePort(serviceName string) string

EnvServicePort returns the environment variable name for a service's port configuration. Example: EnvServicePort("authservice") returns "AUTHSERVICE_PORT"

func GetAppData

func GetAppData[T any](a App, key string) T

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

func GetConfigFieldAsString(c *Config, name string) string

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

func KeyServiceHost(serviceName string) string

KeyServiceHost returns the CLI flag name for a service's host configuration. Example: KeyServiceHost("authservice") returns "authservice-host"

func KeyServicePort

func KeyServicePort(serviceName string) string

KeyServicePort returns the CLI flag name for a service's port configuration. Example: KeyServicePort("authservice") returns "authservice-port"

func ServiceClientHostAndPort

func ServiceClientHostAndPort(a App, serviceName string, tags ...string) func() (string, int)

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

func SetAppData[T any](a App, key string, value T)

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.

func New

func New(
	serviceName string,
) App

New creates a new application instance with the given service name. The service name is used for logging and identification purposes.

Example:

app := app.New("authservice")

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

type Callback func(App) error

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

func (c Config) Parse(flagSet ...*flag.FlagSet) (err error)

Parse parses configuration from environment variables and CLI flags. It loads .env files using godotenv, then processes all registered fields.

The parsing order is:

  1. Load .env file if present
  2. Configure CLI flags with environment variable values as defaults
  3. Parse CLI flags (overrides environment variables)
  4. 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

type DB interface {
	SqlDB() *sql.DB
}

DB is an interface for database connection wrappers. Implementations must provide access to the underlying *sql.DB connection.

type DBCtor

type DBCtor func(App) DB

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

type FlagGroupOverrides = map[string]any

FlagGroupOverrides allows overriding default values for pre-defined field groups. Keys are field names (e.g., KeyHttpPort), values are the new defaults.

type ForwardResponseFn

type ForwardResponseFn func(context.Context, http.ResponseWriter, proto.Message) error

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

type HttpServer interface {
	Server() *http.Server
}

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

type ServiceHTTPHandler func(context.Context, *runtime.ServeMux, string, []grpc.DialOption) error

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

type StartHttpServerFn func(App) error

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.

Jump to

Keyboard shortcuts

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