config

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Nov 9, 2025 License: MIT Imports: 6 Imported by: 0

README

go-config

Enterprise-standard configuration management for Go applications, wrapping Viper with typed configuration, automatic .env file loading, and comprehensive validation.

Features

  • Viper wrapper with sensible defaults
  • Automatic .env file loading with environment variable precedence
  • Typed configuration structs for database, server, and OpenAI
  • Functional options pattern for flexible initialization
  • Comprehensive validation with helpful error messages
  • Zero configuration required - works with defaults out of the box

Installation

go get github.com/JohnPlummer/go-config

Quick Start

package main

import (
    "log"
    "github.com/JohnPlummer/go-config"
)

func main() {
    // Create standard config loader (loads .env files automatically)
    std, err := config.NewStandard()
    if err != nil {
        log.Fatal(err)
    }

    // Load typed database configuration
    dbConfig := config.DatabaseConfigFromViper(std)

    // Validate configuration
    if err := dbConfig.Validate(); err != nil {
        log.Fatal(err)
    }

    // Use the configuration
    log.Printf("Connecting to %s", dbConfig.ConnectionString())
}

Configuration Loading

The Standard loader follows this precedence (highest to lowest):

  1. Environment variables with configured prefix (default: APP_)
  2. .env file values
  3. Config file values (if provided)
  4. Default values
Creating a Standard Config Loader
// With defaults (loads .env, uses APP_ prefix)
std, err := config.NewStandard()

// With custom prefix
std, err := config.NewStandard(
    config.WithEnvPrefix("MYAPP"),
)

// With config file
std, err := config.NewStandard(
    config.WithConfigFile("config.yaml"),
)

// With config name and search paths
std, err := config.NewStandard(
    config.WithConfigName("config"),
    config.WithConfigType("yaml"),
    config.WithConfigPaths(".", "/etc/myapp"),
)

// With custom .env file
std, err := config.NewStandard(
    config.WithEnvFile("/path/to/custom.env"),
)

Database Configuration

Environment Variables
Variable Default Description
DB_HOST localhost Database host
DB_PORT 5432 Database port
DB_NAME or DB_DATABASE postgres Database name
DB_USER or DB_USERNAME postgres Database user
DB_PASSWORD or DB_PASS (none) Database password (required)
DB_SSLMODE disable SSL mode (disable, require, verify-ca, verify-full)
DB_MAX_CONNS 25 Maximum connections in pool
DB_MIN_CONNS 5 Minimum connections in pool
DB_CONN_MAX_LIFETIME 1h Maximum connection lifetime
DB_CONN_MAX_IDLE_TIME 10m Maximum connection idle time
DB_RETRY_ATTEMPTS 3 Number of retry attempts
DB_RETRY_DELAY 2s Delay between retries
DB_HEALTH_CHECK_PERIOD 30s Health check interval
Usage
std, _ := config.NewStandard()
dbConfig := config.DatabaseConfigFromViper(std)

if err := dbConfig.Validate(); err != nil {
    log.Fatal(err)
}

// Get connection string
connStr := dbConfig.ConnectionString()
// postgres://user:pass@localhost:5432/mydb?sslmode=disable

Server Configuration

Environment Variables
Variable Default Description
SERVER_HOST localhost Server host
SERVER_PORT 8080 Server port
SERVER_READ_TIMEOUT 15s Read timeout
SERVER_WRITE_TIMEOUT 15s Write timeout
SERVER_IDLE_TIMEOUT 60s Idle timeout
Usage
std, _ := config.NewStandard()
serverConfig := config.ServerConfigFromViper(std)

if err := serverConfig.Validate(); err != nil {
    log.Fatal(err)
}

// Get address for net/http
addr := serverConfig.Address() // "localhost:8080"

OpenAI Configuration

Environment Variables
Variable Default Description
OPENAI_API_KEY (none) OpenAI API key (required)
OPENAI_MODEL gpt-3.5-turbo Model to use
OPENAI_TEMPERATURE 0.7 Temperature (0.0 - 2.0)
OPENAI_MAX_TOKENS 2000 Maximum tokens in response
OPENAI_TIMEOUT 30s Request timeout
Usage
std, _ := config.NewStandard()
openaiConfig := config.OpenAIConfigFromViper(std)

if err := openaiConfig.Validate(); err != nil {
    log.Fatal(err)
}

client := openai.NewClient(openaiConfig.APIKey)

Validation

All configuration structs provide a Validate() method that checks:

  • Required fields are present
  • Values are within acceptable ranges
  • Cross-field constraints are satisfied
dbConfig := config.DatabaseConfigFromViper(std)

if err := dbConfig.Validate(); err != nil {
    // Error messages are clear and actionable:
    // "database.port must be between 1 and 65535, got 99999"
    // "database.password is required"
    log.Fatal(err)
}
Validation Helpers

The package provides validation helper functions you can use for custom configurations:

// Validate required string field
if err := config.ValidateRequired("field.name", value); err != nil {
    return err
}

// Validate port number (1-65535)
if err := config.ValidatePort("server.port", port); err != nil {
    return err
}

// Validate positive duration
if err := config.ValidateDuration("timeout", duration); err != nil {
    return err
}

// Validate positive integer
if err := config.ValidatePositive("count", count); err != nil {
    return err
}

// Validate value in range
if err := config.ValidateRange("temperature", temp, 0.0, 2.0); err != nil {
    return err
}

Migration from Monorepo

If migrating from the Some Things To Do monorepo:

Before
// pipeline/pkg/config
config, err := config.LoadConfig("config.yaml")
dbConfig := config.Database
After
// github.com/JohnPlummer/go-config
std, err := config.NewStandard(config.WithConfigFile("config.yaml"))
dbConfig := config.DatabaseConfigFromViper(std)

The database configuration struct is compatible - you may only need to update import paths.

Usage Patterns

Environment-Only Configuration

For containerized deployments without config files:

// Just loads from environment variables and .env files
std, err := config.NewStandard()
dbConfig := config.DatabaseConfigFromViper(std)
Config File + Environment Overrides

For local development with environment-specific overrides:

std, err := config.NewStandard(
    config.WithConfigFile("config.yaml"),
)
// Environment variables override config file values
dbConfig := config.DatabaseConfigFromViper(std)
Custom Configuration Structs
type MyConfig struct {
    APIKey  string `mapstructure:"api_key"`
    Timeout int    `mapstructure:"timeout"`
}

std, _ := config.NewStandard()
std.BindEnv("myservice.api_key", "MYSERVICE_API_KEY")
std.BindEnv("myservice.timeout", "MYSERVICE_TIMEOUT")

var myConfig MyConfig
if err := std.Unmarshal(&myConfig); err != nil {
    log.Fatal(err)
}

Examples

See the examples directory for complete, runnable examples:

  • Basic usage - Loading and using typed configurations
  • Validation - Error handling and validation examples

Development

Requirements
  • Go 1.21 or higher
Testing
# Run all tests
go test -v ./...

# Run tests with coverage
go test -v -race -cover ./...

# View coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
Linting
golangci-lint run

License

MIT License - see LICENSE for details.

Contributing

This package is extracted from the Some Things To Do monorepo and follows enterprise Go standards:

  • Comprehensive test coverage (>80%)
  • Clear, actionable error messages
  • Backward-compatible API changes
  • Documentation for all exported types and functions

Documentation

Overview

Package config provides enterprise-standard configuration management wrapping Viper.

This package offers typed configuration with automatic .env file loading, environment variable precedence, and comprehensive validation.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func LoadEnvFile

func LoadEnvFile(paths ...string) error

LoadEnvFile loads environment variables from a .env file. Does not override existing environment variables. Silently succeeds if the file doesn't exist.

func ValidateDuration

func ValidateDuration(field string, duration time.Duration) error

ValidateDuration validates that a duration is positive

func ValidatePort

func ValidatePort(field string, port int) error

ValidatePort validates that a port number is in the valid range (1-65535)

func ValidatePositive

func ValidatePositive(field string, value int) error

ValidatePositive validates that an integer is positive (> 0)

func ValidateRange

func ValidateRange[T int | float64](field string, value, min, max T) error

ValidateRange validates that a value is within a range (inclusive)

func ValidateRequired

func ValidateRequired(field, value string) error

ValidateRequired validates that a string field is not empty

Types

type DatabaseConfig

type DatabaseConfig struct {
	Host     string `mapstructure:"host"`
	Port     int    `mapstructure:"port"`
	Database string `mapstructure:"database"`
	User     string `mapstructure:"user"`
	Password string `mapstructure:"password"`
	SSLMode  string `mapstructure:"ssl_mode"`

	// Connection pool settings
	MaxConns        int           `mapstructure:"max_conns"`
	MinConns        int           `mapstructure:"min_conns"`
	ConnMaxLifetime time.Duration `mapstructure:"conn_max_lifetime"`
	ConnMaxIdleTime time.Duration `mapstructure:"conn_max_idle_time"`

	// Retry settings
	RetryAttempts int           `mapstructure:"retry_attempts"`
	RetryDelay    time.Duration `mapstructure:"retry_delay"`

	// Health check
	HealthCheckPeriod time.Duration `mapstructure:"health_check_period"`
}

DatabaseConfig holds PostgreSQL database configuration with connection pooling settings.

func DatabaseConfigFromViper

func DatabaseConfigFromViper(s *Standard) DatabaseConfig

DatabaseConfigFromViper creates a DatabaseConfig from a Standard config loader.

Environment variable mappings:

  • DB_HOST -> host (default: localhost)
  • DB_PORT -> port (default: 5432)
  • DB_NAME or DB_DATABASE -> database (default: postgres)
  • DB_USER or DB_USERNAME -> user (default: postgres)
  • DB_PASSWORD or DB_PASS -> password
  • DB_SSLMODE -> ssl_mode (default: disable)
  • DB_MAX_CONNS -> max_conns (default: 25)
  • DB_MIN_CONNS -> min_conns (default: 5)
  • DB_CONN_MAX_LIFETIME -> conn_max_lifetime (default: 1h)
  • DB_CONN_MAX_IDLE_TIME -> conn_max_idle_time (default: 10m)
  • DB_RETRY_ATTEMPTS -> retry_attempts (default: 3)
  • DB_RETRY_DELAY -> retry_delay (default: 2s)
  • DB_HEALTH_CHECK_PERIOD -> health_check_period (default: 30s)

func (*DatabaseConfig) ConnectionString

func (c *DatabaseConfig) ConnectionString() string

ConnectionString returns a PostgreSQL connection string

func (*DatabaseConfig) Validate

func (c *DatabaseConfig) Validate() error

Validate validates the database configuration

type OpenAIConfig

type OpenAIConfig struct {
	APIKey      string        `mapstructure:"api_key"`
	Model       string        `mapstructure:"model"`
	Temperature float64       `mapstructure:"temperature"`
	MaxTokens   int           `mapstructure:"max_tokens"`
	Timeout     time.Duration `mapstructure:"timeout"`
}

OpenAIConfig holds OpenAI API configuration

func OpenAIConfigFromViper

func OpenAIConfigFromViper(s *Standard) OpenAIConfig

OpenAIConfigFromViper creates an OpenAIConfig from a Standard config loader.

Environment variable mappings:

  • OPENAI_API_KEY -> api_key (required)
  • OPENAI_MODEL -> model (default: gpt-3.5-turbo)
  • OPENAI_TEMPERATURE -> temperature (default: 0.7)
  • OPENAI_MAX_TOKENS -> max_tokens (default: 2000)
  • OPENAI_TIMEOUT -> timeout (default: 30s)

func (*OpenAIConfig) Validate

func (c *OpenAIConfig) Validate() error

Validate validates the OpenAI configuration

type Option

type Option func(*Standard) error

Option configures the Standard config loader using the functional options pattern.

func WithConfigFile

func WithConfigFile(path string) Option

WithConfigFile specifies a config file to load (YAML, JSON, TOML, etc.)

func WithConfigName

func WithConfigName(name string) Option

WithConfigName sets the name of the config file to search for (without extension)

func WithConfigPaths

func WithConfigPaths(paths ...string) Option

WithConfigPaths adds paths to search for the config file

func WithConfigType

func WithConfigType(configType string) Option

WithConfigType sets the type of the config file (yaml, json, toml, etc.)

func WithEnvFile

func WithEnvFile(path string) Option

WithEnvFile loads environment variables from a specific .env file

func WithEnvPrefix

func WithEnvPrefix(prefix string) Option

WithEnvPrefix sets the environment variable prefix (default: APP_)

func WithoutEnvFile

func WithoutEnvFile() Option

WithoutEnvFile disables automatic .env file loading

type ServerConfig

type ServerConfig struct {
	Host         string        `mapstructure:"host"`
	Port         int           `mapstructure:"port"`
	ReadTimeout  time.Duration `mapstructure:"read_timeout"`
	WriteTimeout time.Duration `mapstructure:"write_timeout"`
	IdleTimeout  time.Duration `mapstructure:"idle_timeout"`
}

ServerConfig holds HTTP server configuration

func ServerConfigFromViper

func ServerConfigFromViper(s *Standard) ServerConfig

ServerConfigFromViper creates a ServerConfig from a Standard config loader.

Environment variable mappings:

  • SERVER_HOST -> host (default: localhost)
  • SERVER_PORT -> port (default: 8080)
  • SERVER_READ_TIMEOUT -> read_timeout (default: 15s)
  • SERVER_WRITE_TIMEOUT -> write_timeout (default: 15s)
  • SERVER_IDLE_TIMEOUT -> idle_timeout (default: 60s)

func (*ServerConfig) Address

func (c *ServerConfig) Address() string

Address returns the server address in host:port format

func (*ServerConfig) Validate

func (c *ServerConfig) Validate() error

Validate validates the server configuration

type Standard

type Standard struct {
	// contains filtered or unexported fields
}

Standard wraps Viper to provide enterprise-standard configuration loading with automatic .env file support, environment variable precedence, and validation.

func NewStandard

func NewStandard(options ...Option) (*Standard, error)

NewStandard creates a new Standard config loader with the given options.

By default: - Loads .env files from current directory (silently ignored if missing) - Reads environment variables with APP_ prefix - Replaces dots and hyphens with underscores in env var names

Options can override any of these defaults.

func (*Standard) AllKeys

func (s *Standard) AllKeys() []string

AllKeys returns all keys in the config

func (*Standard) BindEnv

func (s *Standard) BindEnv(key string, envVars ...string) error

BindEnv binds a config key to environment variables. With no envVars argument, it uses the key as the env var name. With one or more envVars, it checks each in order until finding a set value.

func (*Standard) Get

func (s *Standard) Get(key string) interface{}

Get retrieves a value by key

func (*Standard) GetBool

func (s *Standard) GetBool(key string) bool

GetBool retrieves a boolean value

func (*Standard) GetDuration

func (s *Standard) GetDuration(key string) interface{}

GetDuration retrieves a duration value

func (*Standard) GetInt

func (s *Standard) GetInt(key string) int

GetInt retrieves an integer value

func (*Standard) GetString

func (s *Standard) GetString(key string) string

GetString retrieves a string value

func (*Standard) IsSet

func (s *Standard) IsSet(key string) bool

IsSet checks if a key is set in the config

func (*Standard) Set

func (s *Standard) Set(key string, value interface{})

Set sets a value for a key

func (*Standard) Unmarshal

func (s *Standard) Unmarshal(rawVal interface{}) error

Unmarshal unmarshals the config into a struct

func (*Standard) Viper

func (s *Standard) Viper() *viper.Viper

Viper returns the underlying Viper instance for advanced usage

Jump to

Keyboard shortcuts

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