Stop juggling multiple libraries. Conform unifies configuration loading, type conversion, and validation into a single, declarative interface.
Key Benefits
- β‘ Zero Boilerplate - Declare everything in struct tags, no manual parsing or validation code
- π Type Safety - Full generics support ensures compile-time type checking
- π Production Ready - Built-in support for environment-specific configs, hot reload, and variable substitution
- π‘ Developer Experience - Beautiful, actionable error messages that tell you exactly what's wrong
- π¨ Flexible - Support for multiple sources, custom validators, and converters
- π¦ Lightweight - Minimal dependencies, fast performance
// Load config
viper.SetConfigFile("config.yaml")
viper.ReadInConfig()
// Unmarshal
var cfg Config
viper.Unmarshal(&cfg)
// Validate
validate := validator.New()
if err := validate.Struct(cfg); err != nil {
// Parse errors...
}
// Type conversion? Manual!
port, _ := strconv.Atoi(viper.GetString("port"))
timeout, _ := time.ParseDuration(viper.GetString("timeout"))
type Config struct {
Port int `conform:"env=PORT,default=8080,validate=gte:1024"`
Timeout time.Duration `conform:"env=TIMEOUT,default=30s"`
Database string `conform:"env=DB_URL,required,validate=url"`
}
cfg, err := conform.LoadGeneric[Config](conform.FromEnv())
// β¨ Done! Type-safe, validated, ready to use.
One struct tag. One function call. Zero boilerplate.
Perfect For
- π’ Microservices - Type-safe configuration across services
- π Cloud-Native Apps - Environment-specific configs for Kubernetes, Docker
- π§ CLI Tools - Easy configuration management
- π APIs & Web Services - Fast, validated config loading
- π§ͺ Testing - Mock-friendly configuration loading
β¨ Features
π― Core Features
| Feature |
Description |
| π·οΈ Declarative Configuration |
Everything in struct tags, zero boilerplate |
| π Type-Safe Generics |
Full type safety with Go 1.21+ generics |
| π Multi-Source Support |
Environment variables, files (YAML/JSON/TOML), custom sources |
| β
Built-in Validation |
20+ validators out of the box |
| π§ Smart Type Coercion |
Automatic conversion for complex types |
| π¦ Nested Structs |
Full support with automatic prefix handling |
| π₯ Hot Reload |
Watch for changes and reload automatically |
| π¬ Beautiful Errors |
Detailed error messages with suggestions |
| π Environment-Specific |
Load different configs for dev/staging/prod |
| π Variable Substitution |
${VAR_NAME:-default} syntax support |
| Feature |
Conform |
Viper |
envconfig |
koanf |
| Type Safety |
β
Generics |
β |
β |
β |
| Validation |
β
Built-in |
β Requires validator |
β |
β οΈ Basic |
| Error Messages |
β
Beautiful |
β |
β |
β οΈ Basic |
| Hot Reload |
β
Built-in |
β
WatchConfig |
β |
β
|
| Environment-Specific |
β
Built-in |
β οΈ Manual |
β |
β
|
| Variable Substitution |
β
Built-in |
β |
β |
β
|
| Declarative |
β
100% |
β οΈ Partial |
β
|
β
|
| Zero Boilerplate |
β
|
β |
β οΈ |
β οΈ |
Note:
- Viper requires separate validation library (e.g.,
go-playground/validator)
- envconfig is minimal and focused only on environment variables
- koanf is a modern alternative but lacks generics and built-in validation
π¦ Installation
Go Module
go get github.com/alicanli1995/conform
Import
import "github.com/alicanli1995/conform"
Requirements
- Go 1.21 or higher
- No external dependencies required (except for file format support: YAML, TOML)
π Quick Start
Basic Example
Define your configuration struct with conform tags:
package main
import (
"fmt"
"os"
"github.com/alicanli1995/conform"
)
type Config struct {
Port int `conform:"env=APP_PORT,default=8080,validate=gte:1024"`
Host string `conform:"env=APP_HOST,default=localhost,validate=hostname"`
Database string `conform:"env=DATABASE_URL,required,validate=url"`
}
func main() {
os.Setenv("APP_PORT", "3000")
os.Setenv("DATABASE_URL", "postgres://localhost/mydb")
cfg, err := conform.LoadGeneric[Config](conform.FromEnv())
if err != nil {
panic(err) // Beautiful error messages!
}
fmt.Printf("Server: %s:%d\n", cfg.Host, cfg.Port)
}
That's it! Your configuration is loaded, validated, and ready to use. π
What Just Happened?
- β
Loaded from environment variables
- β
Converted types automatically (
string β int)
- β
Validated against rules (
gte:1024, hostname, url)
- β
Applied defaults where needed
- β
Returned type-safe config struct
Try It Yourself
# Run the example
cd examples/basic && go run main.go
π Documentation
Multi-Source Support
Load from multiple sources with automatic priority (first source wins):
cfg, err := conform.LoadGeneric[Config](
conform.FromEnv(), // Highest priority
conform.FromFile("secrets.json"), // Second priority
conform.FromFile("config.yaml"), // Third priority
conform.WithSource(&CustomSource{}), // Custom source
)
// Priority: env > custom sources > file sources > defaults
Environment-Specific Configuration
Perfect for dev/staging/production environments:
type Config struct {
Database struct {
Host string `conform:"file=database.host,default=localhost"`
Port int `conform:"file=database.port,default=5432"`
}
}
// Development
devCfg, _ := conform.LoadGeneric[Config](
conform.WithEnvironment("development"),
conform.FromFile("config.${ENV}.yaml"), // Loads config.development.yaml
)
// Production
prodCfg, _ := conform.LoadGeneric[Config](
conform.WithEnvironment("production"),
conform.FromFile("config.${ENV}.yaml"), // Loads config.production.yaml
)
Variable Substitution
Use ${VAR_NAME:-default} syntax in config values:
type Config struct {
DatabaseURL string `conform:"env=DB_URL,default=postgres://${DB_USER:-postgres}:${DB_PASSWORD}@${DB_HOST:-localhost}:${DB_PORT:-5432}/${DB_NAME:-mydb}"`
APIURL string `conform:"env=API_URL,default=https://api.${ENV:-dev}.example.com"`
}
Smart Type Coercion
Automatic conversion for complex types:
type Config struct {
// String "true" β bool true
Debug bool `conform:"env=DEBUG"`
// String "30s" β time.Duration
Timeout time.Duration `conform:"env=TIMEOUT"`
// String "1,2,3" β []int{1,2,3}
IDs []int `conform:"env=IDS,separator=,"`
// String "1=one,2=two" β map[int]string{1:"one", 2:"two"}
Mapping map[int]string `conform:"env=MAPPING"`
// String "2024-01-01" β time.Time
StartDate time.Time `conform:"env=START,format=2006-01-02"`
}
Beautiful Error Messages
Get detailed, actionable error messages:
cfg, err := conform.LoadGeneric[Config](conform.FromEnv())
if err != nil {
fmt.Println(err)
// Output:
// β Configuration validation failed:
//
// 1. Port (APP_PORT): value 80 is too small
// Got: 80
// Location: env var APP_PORT
// π‘ Suggestion: Use a value >= 1024 (e.g. 8080)
//
// 2. Database.URL (DB_URL): invalid URL format
// Got: "not-a-url"
// Expected: valid URL with scheme
// π‘ Suggestion: Format should be: https://example.com
}
Hot Reload
Watch for configuration changes automatically:
watcher, err := conform.Watch[Config](func(newCfg Config) {
log.Printf("Config reloaded: %+v", newCfg)
// Update your application state here
}, conform.FromEnv(), conform.FromFile("config.yaml"))
// Thread-safe access
cfg := watcher.Get()
// Stop watching
defer watcher.Stop()
Custom Validators
Register your own validation rules:
conform.RegisterValidator("strong_password", func(val interface{}, params []string) error {
str := val.(string)
if len(str) < 12 {
return fmt.Errorf("password must be at least 12 characters")
}
if !hasSpecialChar(str) {
return fmt.Errorf("password must contain special character")
}
return nil
})
type Config struct {
Password string `conform:"env=PASSWORD,validate=strong_password"`
}
Custom Converters
Convert to custom types:
type CustomType string
conform.RegisterConverter(
reflect.TypeOf(CustomType("")),
func(s string) (interface{}, error) {
return CustomType("custom_" + s), nil
},
)
type Config struct {
Custom CustomType `conform:"env=CUSTOM"`
}
Nested Configuration
Full support for nested structs with automatic prefix handling:
type DatabaseConfig struct {
Host string `conform:"env=HOST,default=localhost"`
Port int `conform:"env=PORT,default=5432"`
}
type AppConfig struct {
Name string `conform:"env=APP_NAME,default=MyApp"`
Database DatabaseConfig `conform:"prefix=DB_"`
}
// Environment variables:
// DB_HOST=db.example.com
// DB_PORT=5432
File Configuration
Support for YAML, JSON, and TOML:
type Config struct {
Server struct {
Host string `conform:"file=server.host,default=localhost"`
Port int `conform:"file=server.port,default=8080"`
}
}
// YAML
cfg, _ := conform.LoadGeneric[Config](conform.FromFile("config.yaml"))
// TOML
cfg, _ := conform.LoadGeneric[Config](conform.FromFile("config.toml"))
// JSON
cfg, _ := conform.LoadGeneric[Config](conform.FromFile("config.json"))
π Tag Reference
| Tag |
Description |
Example |
env=VAR_NAME |
Load from environment variable |
env=APP_PORT |
file=key.path |
Load from config file (dot notation) |
file=database.host |
default=value |
Default value if not found |
default=8080 |
required |
Field is required (error if missing) |
required |
prefix=PREFIX_ |
Prefix for nested structs |
prefix=DB_ |
| Tag |
Description |
Example |
format=layout |
Format for time.Time |
format=2006-01-02 |
separator=, |
Separator for slices |
separator=| |
| Tag |
Description |
Example |
validate=rule:param |
Validation rules |
validate=gte:1024,lte:65535 |
β
Built-in Validators
Numeric Validators
min:value - Minimum value/length
max:value - Maximum value/length
gte:value - Greater than or equal
lte:value - Less than or equal
eq:value - Equal to
ne:value - Not equal to
String Validators
email - Valid email address
url - Valid URL (use url:https for HTTPS only)
ip - Valid IP address (IPv4 or IPv6)
hostname - Valid hostname
port - Valid port number (1-65535)
alphanum - Only letters and digits
alpha - Only letters
numeric - Only digits
regex:pattern - Match regex pattern
oneof:val1:val2 - One of the specified values
len:length - Exact length
Password Validators
has_upper - Contains uppercase letter
has_lower - Contains lowercase letter
has_digit - Contains digit
has_special - Contains special character
General
required - Field is required
Note: CLI tool is currently in development. For now, use the programmatic API for validation.
Validate configuration files programmatically:
cfg, err := conform.LoadGeneric[Config](
conform.FromFile("config.yaml"),
conform.FromEnv(),
)
if err != nil {
fmt.Println(err) // Beautiful error messages
}
π Examples
Comprehensive examples available in the examples directory:
| Example |
Description |
Link |
| π Basic Usage |
Getting started with Conform |
View |
| π― Generic API |
Type-safe loading with generics |
View |
| π File Configuration |
YAML, JSON, TOML examples |
View |
| π Environment-Specific |
Dev/staging/prod configs |
View |
| β‘ Advanced Features |
Complex scenarios |
View |
| π§ Custom Extensions |
Custom converters & validators |
View |
| π₯ Hot Reload |
Dynamic configuration |
View |
| π¬ Error Handling |
Beautiful error messages |
View |
| π’ Real-World |
Production-ready example |
View |
Run All Examples
make run-examples
π License
This project is licensed under the MIT License - see the LICENSE file for details.