Documentation
¶
Overview ¶
Package goconfig provides a simple way to load configuration from environment variables using struct tags.
Features ¶
- Load configuration from environment variables using struct tags
- Support for nested structs
- Optional default values
- Type conversion for common types: string, bool, int, uint, float, time.Duration
- Ability to convert JSON strings into maps or JSON annotated structs
- Built-in min, max, pattern validators plus support for custom validation
- Support for custom field parsing
- Support for a custom key-value store as an alternative to environment variables
- Clear error messages for missing required fields or invalid values
Basic Usage ¶
Define your configuration struct with 'key' and optional 'default' tags:
type Config struct {
APIKey string `key:"API_KEY"` // Required
Model string `key:"MODEL" default:"gpt-4"` // Optional with default
Port int `key:"PORT" default:"8080" min:"1024" max:"65535"` // With min/max validation
Timeout time.Duration `key:"TIMEOUT" default:"30s" min:"1s" max:"5m"` // Duration with validation
}
func main() {
var config Config
if err := goconfig.Load(context.Background(), &config); err != nil {
log.Fatalf("Failed to load configuration: %v", err)
}
// Port is guaranteed to be between 1024 and 65535
// Timeout is guaranteed to be between 1s and 5m
}
Struct Tags ¶
- key: The environment variable name to read from (required)
- default: The default value to use if the environment variable is not set (optional)
- min: Minimum value for numeric types (optional)
- max: Maximum value for numeric types (optional)
- pattern: Regular expression for string types (optional)
- required: Set to "true" to require the field to not be empty (optional)
- keyRequired: Set to "true" to require the field to be present, though it can be explicitly blank
Supported Types ¶
- string
- bool
- int, int8, int16, int32, int64
- uint, uint8, uint16, uint32, uint64
- float32, float64
- time.Duration (uses Go's duration format: "30s", "1m", "1h", etc.)
- map[string]interface{} using JSON deserialisation
- struct using JSON deserialisation
- pointers to the above
Custom Validation ¶
Use the WithValidator option to add custom validation logic:
err := goconfig.Load(ctx, &cfg,
goconfig.WithValidator("APIKey", func(value any) error {
key := value.(string)
if !strings.HasPrefix(key, "sk-") {
return fmt.Errorf("API key must start with 'sk-'")
}
return nil
}),
)
Custom Parsers ¶
Use the WithParser option to provide custom parsing logic for specific fields:
err := goconfig.Load(ctx, &cfg,
goconfig.WithParser("SpecialField", func(value string) (any, error) {
// Custom parsing logic
return customParse(value)
}),
)
Custom Key Stores ¶
By default, the package reads from environment variables, but you can provide a custom key-value store:
myStore := func(ctx context.Context, key string) (string, bool, error) {
// Custom logic to retrieve configuration values
return value, found, nil
}
err := goconfig.Load(ctx, &config, goconfig.WithKeyStore(myStore))
You can also chain multiple key stores using CompositeStore, which tries each store in order until one returns a value:
store := goconfig.CompositeStore(
customStore,
goconfig.EnvironmentKeyStore,
)
err := goconfig.Load(ctx, &config, goconfig.WithKeyStore(store))
Error Handling ¶
The package provides two sentinel errors for common cases:
- ErrMissingConfigKey: returned when a required key is not found in the key store
- ErrMissingValue: returned when a key is found but has a blank value when required="true"
When multiple configuration errors occur, they are collected into a ConfigErrors type, which implements error and provides an Unwrap method for Go 1.20+ error inspection:
err := goconfig.Load(ctx, &config)
if err != nil {
var configErrs *goconfig.ConfigErrors
if errors.As(err, &configErrs) {
for _, e := range configErrs.Unwrap() {
if errors.Is(e, goconfig.ErrMissingConfigKey) {
// Handle missing key
}
}
}
}
Error logging to structured logs is supported.
Documentation ¶
For detailed guides and examples, see:
- https://github.com/m0rjc/goconfig/tree/main/docs - Full documentation
- https://github.com/m0rjc/goconfig/tree/main/docs/validation.md - Validation guide
- https://github.com/m0rjc/goconfig/tree/main/docs/defaulting.md - Defaulting behavior
- https://github.com/m0rjc/goconfig/tree/main/docs/json.md - JSON deserialization
- https://github.com/m0rjc/goconfig/tree/main/docs/advanced.md - Advanced features
- https://github.com/m0rjc/goconfig/tree/main/example - Working examples
Index ¶
- Variables
- func EnvironmentKeyStore(_ context.Context, key string) (string, bool, error)
- func Load(ctx context.Context, config interface{}, options ...Option) error
- func LogError(logger *slog.Logger, err error, opts ...ErrorLogOption)
- func RegisterCustomType[T any](handler TypedHandler[T])
- type ConfigError
- type ConfigErrors
- type ErrorLogOption
- type FieldProcessor
- type KeyStore
- type Option
- type Transform
- type TypedHandler
- func AddDynamicValidation[T any](baseHandler TypedHandler[T], wrapper Wrapper[T]) TypedHandler[T]
- func AddValidators[T any](baseHandler TypedHandler[T], customValidators ...Validator[T]) TypedHandler[T]
- func CastCustomType[T, U any](baseHandler TypedHandler[T]) TypedHandler[U]
- func DefaultDurationType() TypedHandler[time.Duration]
- func DefaultFloatIntegerType[T ~float32 | ~float64]() TypedHandler[T]
- func DefaultIntegerType[T ~int | ~int8 | ~int16 | ~int32 | ~int64]() TypedHandler[T]
- func DefaultStringType[T ~string]() TypedHandler[T]
- func DefaultUnsignedIntegerType[T ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64]() TypedHandler[T]
- func NewCustomType[T any](customParser FieldProcessor[T], customValidators ...Validator[T]) TypedHandler[T]
- func NewStringEnumType[T ~string](validValues ...T) TypedHandler[T]
- func TransformCustomType[T, U any](baseHandler TypedHandler[T], transform Transform[T, U]) TypedHandler[U]
- type Validator
- type Wrapper
Constants ¶
This section is empty.
Variables ¶
var ( ErrMissingConfigKey = errors.New("no configuration found for this key") ErrMissingValue = errors.New("missing or blank value for this key") )
Functions ¶
func EnvironmentKeyStore ¶
EnvironmentKeyStore is a key store that reads values from environment variables
func Load ¶
Load populates the given configuration struct from environment variables using the `key`, `default`, `required`, `min`, `max`, and `pattern` struct tags.
Value resolution follows this precedence (highest to lowest):
- Environment variable (if set)
- Tag default (if specified with default:"value")
- Pre-initialized struct value (allows coded defaults)
Fields without any value source are left unchanged, allowing you to set defaults by initializing the struct before calling Load.
Use required:"true" to enforce that a field must be set via environment variable or default tag.
Builtin Validation Tags:
- min:"value" and max:"value": Numeric range validation (int, uint, float types)
- pattern:"regex": Regular expression validation (string types only)
Custom Validation:
- WithValidator(path, validator): Add a validator for a specific field path
- WithValidatorFactory(factory): Register a factory to auto-add validators based on field metadata
- Validators run after type conversion but before field assignment
Options:
- WithValidator(path, validator): Register custom validator for a specific field
- WithValidatorFactory(factory): Register a custom validator factory
Example:
type Config struct {
Port int `key:"PORT" default:"8080" min:"1024" max:"65535"`
Username string `key:"USERNAME" pattern:"^[a-zA-Z0-9_]+$"`
Email string `key:"EMAIL"`
}
cfg := Config{}
err := Load(&cfg, WithValidator("Email", emailValidator))
func LogError ¶
func LogError(logger *slog.Logger, err error, opts ...ErrorLogOption)
LogError is a convenience function to log either a single error from the configuration load or the collection of validation errors. If a collection of validation errors is returned then they will be logged individually using ConfigErrors.LogAll().
func RegisterCustomType ¶
func RegisterCustomType[T any](handler TypedHandler[T])
Types ¶
type ConfigError ¶
type ConfigError struct {
Key string // Environment variable name (e.g., "DB_PORT", "API_KEY")
Err error // The underlying error
}
ConfigError represents a single configuration error for a specific environment variable.
type ConfigErrors ¶
type ConfigErrors struct {
// Errors contains the collected errors
Errors []ConfigError
}
ConfigErrors collects multiple runtime configuration errors. It maintains the order errors were encountered and provides formatted output.
func (*ConfigErrors) Add ¶
func (ce *ConfigErrors) Add(key string, err error)
Add adds a new error for the given environment variable.
func (*ConfigErrors) Error ¶
func (ce *ConfigErrors) Error() string
Error implements the error interface. It formats all collected errors as: "KEY1: error1; KEY2: error2"
func (*ConfigErrors) HasErrors ¶
func (ce *ConfigErrors) HasErrors() bool
HasErrors returns true if any errors were collected.
func (*ConfigErrors) Len ¶
func (ce *ConfigErrors) Len() int
Len returns the number of errors collected.
func (*ConfigErrors) LogAll ¶
func (ce *ConfigErrors) LogAll(logger *slog.Logger, opts ...ErrorLogOption)
LogAll logs each configuration error using structured logging.
Example usage:
logger := slog.New(slog.NewJSONHandler(os.Stderr, nil))
if err := LoadConfig(&cfg); err != nil {
if configErrs, ok := err.(*ConfigErrors); ok {
configErrs.LogAll(logger, WithLogMessage("configuration_error"))
}
}
func (*ConfigErrors) Unwrap ¶
func (ce *ConfigErrors) Unwrap() []error
Unwrap returns all underlying errors for Go 1.20+ error inspection.
type ErrorLogOption ¶
type ErrorLogOption func(*logSettings)
ErrorLogOption provides options for the ConfigErrors.LogAll method
func WithLogMessage ¶
func WithLogMessage(message string) ErrorLogOption
WithLogMessage sets the log message to be passed to the Logger in the ConfigErrors.LogAll method
type FieldProcessor ¶
type FieldProcessor[T any] = readpipeline.FieldProcessor[T]
func AddValidatorToPipeline ¶ added in v0.4.0
func AddValidatorToPipeline[T any](pipeline FieldProcessor[T], validator Validator[T]) FieldProcessor[T]
AddValidatorToPipeline adds a validator to a pipeline. This is used as part of pipeline building in the TypedHandler.
type KeyStore ¶
KeyStore reads string values given keys. Return the value if present (it may be empty), an indication of whether it is present or an error if there was an error accessing the store.
func CompositeStore ¶
CompositeStore tries each store in turn until one returns a value or an error.
func NewEnvFileKeyStore ¶ added in v0.4.0
NewEnvFileKeyStore returns a KeyStore that reads values from a list of environment files. If no filenames are provided, it defaults to ".env". Files are processed in the order they are provided. If multiple files contain the same key, the first one encountered wins.
type Option ¶
type Option func(*loadOptions)
Option is a functional option for configuring the Load function.
func WithCustomType ¶
func WithCustomType[T any](handler TypedHandler[T]) Option
WithCustomType registers a custom type handler for a given type.
func WithKeyStore ¶
WithKeyStore replaces the environment variable keystore with an alternative. Use this to read from other sources such as a database or properties file.
type Transform ¶ added in v0.4.0
type Transform[T, U any] = customtypes.Transform[T, U]
type TypedHandler ¶
type TypedHandler[T any] = readpipeline.TypedHandler[T]
func AddDynamicValidation ¶
func AddDynamicValidation[T any](baseHandler TypedHandler[T], wrapper Wrapper[T]) TypedHandler[T]
AddDynamicValidation allows a TypedHandler to add validation (or other logic) to the process pipeline dependent on struct tags present on the target field. See the AddValidatorToPipeline function and the example/custom_tags example for more details.
func AddValidators ¶
func AddValidators[T any](baseHandler TypedHandler[T], customValidators ...Validator[T]) TypedHandler[T]
func CastCustomType ¶
func CastCustomType[T, U any](baseHandler TypedHandler[T]) TypedHandler[U]
func DefaultDurationType ¶
func DefaultDurationType() TypedHandler[time.Duration]
func DefaultFloatIntegerType ¶
func DefaultFloatIntegerType[T ~float32 | ~float64]() TypedHandler[T]
func DefaultIntegerType ¶
func DefaultStringType ¶
func DefaultStringType[T ~string]() TypedHandler[T]
func NewCustomType ¶
func NewCustomType[T any](customParser FieldProcessor[T], customValidators ...Validator[T]) TypedHandler[T]
func NewStringEnumType ¶
func NewStringEnumType[T ~string](validValues ...T) TypedHandler[T]
func TransformCustomType ¶ added in v0.4.0
func TransformCustomType[T, U any](baseHandler TypedHandler[T], transform Transform[T, U]) TypedHandler[U]
TransformCustomType creates a TypedHandler that applies a Transform function to process data from a base handler.
type Validator ¶
type Validator[T any] = readpipeline.Validator[T]
type Wrapper ¶
type Wrapper[T any] = readpipeline.Wrapper[T]
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
example
|
|
|
custom_tags
command
|
|
|
custom_types
command
|
|
|
simple
command
|
|
|
structured_logging
command
|
|
|
validation
command
|
|
|
internal
|
|
|
customtypes
Package customtypes provides the building blocks to make custom types for GoConfig.
|
Package customtypes provides the building blocks to make custom types for GoConfig. |