Documentation
¶
Overview ¶
Package ligo_config provides a Ligo-native configuration service inspired by @nestjs/config. It loads layered configuration from .env files, process env, and programmatic loaders, then exposes a *Service that other Ligo providers consume via dependency injection.
app.Register(
ligo_config.Module(
ligo_config.WithEnvFiles(".env.local", ".env"),
ligo_config.WithExpand(true),
),
myFeatureModule(),
)
func NewMyService(cfg *ligo_config.Service) *MyService {
return &MyService{
host: cfg.GetOr("HOST", "localhost"),
port: cfg.MustGetInt("PORT"),
}
}
Index ¶
- Constants
- Variables
- func Bind[T any](svc *Service) (*T, error)
- func Module(opts ...Option) ligo.Module
- func ModuleWith(svc *Service) ligo.Module
- func MustBind[T any](svc *Service) *T
- func Namespace[T any]() ligo.Provider
- func NamespaceFn[T any](fn NamespaceFactory[T]) ligo.Provider
- func Provider() ligo.Provider
- type Loader
- type NamespaceFactory
- type Option
- type Service
- func (s *Service) All() map[string]string
- func (s *Service) Get(key string) (string, bool)
- func (s *Service) GetBool(key string) (bool, error)
- func (s *Service) GetBoolOr(key string, def bool) bool
- func (s *Service) GetDuration(key string) (time.Duration, error)
- func (s *Service) GetDurationOr(key string, def time.Duration) time.Duration
- func (s *Service) GetInt(key string) (int, error)
- func (s *Service) GetIntOr(key string, def int) int
- func (s *Service) GetOr(key, def string) string
- func (s *Service) MustGet(key string) (string, error)
- type Validator
Constants ¶
const ModuleName = "ligo_config"
ModuleName is the registered name of the config module within Ligo's module tree. Useful for module composition diagnostics.
Variables ¶
var ( // ErrKeyNotFound is returned by MustGet when a key is absent. ErrKeyNotFound = errors.New("config: key not found") // ErrInvalidValue is returned when a value cannot be coerced to the // requested type (e.g. GetInt on a non-numeric string). ErrInvalidValue = errors.New("config: invalid value") // ErrLoadFailed wraps any failure that occurs while a Loader or // .env file is being parsed during Module initialization. ErrLoadFailed = errors.New("config: load failed") // ErrValidation wraps validator/v10 errors raised after Bind. ErrValidation = errors.New("config: validation failed") // ErrUnresolvedVariable is returned when ${VAR:?msg} expansion fails // because VAR is missing and a fail-on-unset marker is present. ErrUnresolvedVariable = errors.New("config: unresolved variable") )
Functions ¶
func Bind ¶
Bind decodes the configuration into a fresh *T by reading struct field tags. Supported tags:
- env:"KEY" — source key (required for the field to be set)
- default:"value" — used if the env key is unset
- validate:"..." — go-playground/validator rules, applied after decode
Supported field types: string, bool, int/int8/int16/int32/int64, uint variants, float32, float64, time.Duration, time.Time (RFC3339), and slices of any of those (split by `,`).
type DBConfig struct {
Host string `env:"DB_HOST" default:"localhost" validate:"required"`
Port int `env:"DB_PORT" default:"5432"`
URL string `env:"DB_URL" validate:"required,url"`
}
cfg, err := ligo_config.Bind[DBConfig](svc)
func Module ¶
Module returns a Ligo module that loads configuration from .env files, programmatic [Loader]s, and process env, then publishes a singleton *Service into the DI container.
It is the Go equivalent of ConfigModule.forRoot from @nestjs/config — register it once at the top of your module tree and inject *Service into downstream providers.
app.Register(
ligo_config.Module(
ligo_config.WithEnvFiles(".env.local", ".env"),
ligo_config.WithExpand(true),
ligo_config.WithValidate(func(s *ligo_config.Service) error {
_, err := s.MustGet("DATABASE_URL")
return err
}),
),
myFeatureModule(),
)
Loading happens during OnInit, so failures (missing required keys, validator errors, malformed .env files) abort startup before any downstream factory is constructed.
func ModuleWith ¶ added in v0.3.0
ModuleWith returns a Ligo module that publishes a pre-built *Service into the DI container, skipping the loader pipeline. Pair with Load or MustLoad when you need to read config BEFORE ligo.New (e.g. for ligo.WithAddr) and want to reuse the same loaded values inside the app:
cfg := ligo_config.MustLoad(
ligo_config.WithEnvFiles(".env.local", ".env"),
ligo_config.WithExpand(true),
)
addr := ":" + cfg.GetOr("PORT", "8080")
app := ligo.New(ligo.WithAddr(addr))
app.Register(
ligo_config.ModuleWith(cfg),
myFeatureModule(),
)
This avoids the double-load and option-drift you'd get from calling Load and Module with parallel option lists. Use Module when no boot-time read is needed.
func MustBind ¶
MustBind is the panicking variant of Bind. Use in package-level initialization where a missing config is genuinely fatal.
func Namespace ¶
Namespace returns a ligo.Provider that produces a *T by calling Bind on the injected *Service. Other factories in the same module can then receive the typed config directly:
type DBConfig struct {
Host string `env:"DB_HOST" default:"localhost"`
Port int `env:"DB_PORT" default:"5432"`
}
func DatabaseModule() ligo.Module {
return ligo.NewModule("database",
ligo.Providers(
ligo_config.Namespace[DBConfig](),
ligo.Factory[*Repo](NewRepo),
),
)
}
func NewRepo(cfg *DBConfig) *Repo { /* … */ }
This mirrors registerAs from @nestjs/config — types act as the namespace label so each typed block is independently injectable.
func NamespaceFn ¶
func NamespaceFn[T any](fn NamespaceFactory[T]) ligo.Provider
NamespaceFn is the same as Namespace but lets you supply a custom factory instead of going through Bind. Use when the env tag model doesn't fit (cross-field derivation, runtime feature flags, etc.).
ligo_config.NamespaceFn[DBConfig](func(svc *ligo_config.Service) (*DBConfig, error) {
return &DBConfig{
URL: fmt.Sprintf("postgres://%s:%d/%s",
svc.GetOr("DB_HOST", "localhost"),
svc.GetIntOr("DB_PORT", 5432),
svc.GetOr("DB_NAME", "app")),
}, nil
})
func Provider ¶
Provider returns just the *Service provider without the surrounding module. Use when you want to embed config setup inside an existing module instead of registering a dedicated one.
This variant skips loaders, .env files, and validation — it just publishes a *Service backed by os.Environ. Reach for Module when you need the full pipeline (env files, expansion, validators).
Types ¶
type Loader ¶
Loader is a programmatic configuration source. Loaders run during Module initialization and contribute key/value pairs that override values from .env files (but not process env, unless WithLoadersWin is set).
type NamespaceFactory ¶
NamespaceFactory is a custom builder for a typed configuration block. It receives the merged *Service and returns a populated *T.
type Option ¶
type Option func(*options)
Option configures the Module returned by Module / [ConfigModule].
func WithEnvFiles ¶
WithEnvFiles sets the list of .env files to attempt to load, in order from highest precedence to lowest. The first existing file's keys win among files. Missing files are skipped silently.
Default: [".env"].
func WithExpand ¶
WithExpand enables POSIX-style variable expansion inside values:
- ${VAR} — substitute VAR; empty string if unset
- ${VAR:-def} — substitute VAR; "def" if unset or empty
- ${VAR:?msg} — substitute VAR; fail load with msg if unset or empty
Variables resolve against the already-merged map at expansion time, so later-loaded sources can reference earlier ones.
Default: false (literal values, no expansion).
func WithIgnoreEnvFile ¶
WithIgnoreEnvFile skips .env file loading entirely. Use in containerized environments where all configuration comes from the process env or explicit loaders.
func WithLoader ¶
WithLoader appends a programmatic configuration source. Loaders run in the order they are registered; later loaders override earlier ones.
By default loaders override .env files but not process env. Use WithLoadersWin to flip that precedence.
func WithLoadersWin ¶
WithLoadersWin makes Loader-supplied keys override process env. Useful in tests where you want to inject a deterministic config map regardless of the host's environment.
func WithValidate ¶
WithValidate registers a hook that runs after all sources have been merged but before the Service is exposed to other providers. Returning a non-nil error aborts ligo.App.Run.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the merged, immutable view over all configured sources. It is registered as a singleton provider by Module; inject *Service into any factory in the same module tree.
Service is safe for concurrent use.
func Load ¶ added in v0.2.0
Load synchronously builds a *Service outside the Ligo DI lifecycle. Use in main() when you need configuration values BEFORE ligo.New — for example, to resolve the bind address passed to ligo.WithAddr, which wires at construction time, earlier than Module's OnInit hook.
svc, err := ligo_config.Load(ligo_config.WithEnvFiles(".env"))
if err != nil {
panic(err)
}
addr := ":" + svc.GetOr("PORT", "8080")
app := ligo.New(ligo.WithAddr(addr), ...)
The returned *Service is independent from the one Module produces; they read the same sources but do not share state. If you only need config at runtime (inside handlers, use cases, providers), prefer injecting *Service via Module instead.
func MustLoad ¶ added in v0.2.0
MustLoad is the panicking variant of Load. Use in main() where a missing or malformed config should crash the process before ligo.New.
func (*Service) All ¶
All returns a defensive copy of the merged key/value map. Intended for debugging and validation hooks; not a hot path.
func (*Service) Get ¶
Get returns the raw string value for key and a presence flag. Empty strings count as present.
func (*Service) GetBool ¶
GetBool parses the value as a bool (1/0/true/false/yes/no/on/off, case-insensitive). Returns ErrKeyNotFound or ErrInvalidValue.
func (*Service) GetBoolOr ¶
GetBoolOr returns the bool value for key, or def on miss or parse error.
func (*Service) GetDuration ¶
GetDuration parses the value via time.ParseDuration (e.g. "5s", "300ms", "1h30m"). Returns ErrKeyNotFound or ErrInvalidValue.
func (*Service) GetDurationOr ¶
GetDurationOr returns the time.Duration value for key, or def on miss or parse error.
func (*Service) GetInt ¶
GetInt parses the value as an int. Returns ErrKeyNotFound if absent and ErrInvalidValue if non-numeric.