Creating Configuration Containers¶
Factory Function Selection Guide¶
GTB provides several factory functions for creating configuration containers. This section helps you choose the right one for your use case.
Quick Reference¶
| Factory Function | Use Case | Error Handling | File Watching |
|---|---|---|---|
NewFilesContainer(fs, opts...) |
Application startup with optional files | Logs warnings, continues | ✓ Enabled |
LoadFilesContainer(fs, opts...) |
Strict loading where config is required | Returns error | ✗ Disabled |
LoadFilesContainerWithSchema(fs, schema, opts...) |
Strict loading with schema validation | Returns error | ✗ Disabled |
NewReaderContainer(fs, opts...) |
Testing or embedded config streams | Logs warnings, continues | ✗ Disabled |
NewContainerFromViper(l, v) |
Wrapping existing Viper instances | N/A | Depends on Viper |
NewFilesContainer¶
Best for: Production applications where some config files may not exist.
container := config.NewFilesContainer(fs,
config.WithLogger(l),
config.WithConfigFiles("config.yaml", "config.local.yaml"),
)
Behavior:
- Silently continues if files don't exist
- Logs warnings for parse errors but doesn't fail
- Automatically enables file watching for hot-reload
- Merges files in order (later files override earlier ones)
LoadFilesContainer¶
Best for: Scenarios where configuration is mandatory.
container, err := config.LoadFilesContainer(fs,
config.WithLogger(l),
config.WithConfigFiles("config.yaml", "config.local.yaml"),
)
if err != nil {
return fmt.Errorf("configuration required: %w", err)
}
Behavior:
- Returns
ErrConfigFileNotFound(match witherrors.Is) if the first file doesn't exist — never a nil container with a nil error - Subsequent files are optional (merged if present)
- No file watching (single load operation)
- Preferred for CLI tools that require explicit configuration
NewReaderContainer¶
Best for: Testing and programmatic configuration.
// From strings (testing)
configYAML := `
app:
name: test-app
debug: true
`
container := config.NewReaderContainer(fs,
config.WithLogger(l),
config.WithConfigFormat("yaml"),
config.WithConfigReaders(strings.NewReader(configYAML)),
)
// From embedded bytes
container := config.NewReaderContainer(fs,
config.WithLogger(l),
config.WithConfigFormat("yaml"),
config.WithConfigReaders(
bytes.NewReader(defaultConfigBytes),
bytes.NewReader(envSpecificBytes),
),
)
Behavior:
- Accepts
io.Readerinstead of file paths - Must specify format explicitly ("yaml", "json", "toml")
- No file watching (readers are consumed once)
- Ideal for unit tests with controlled configuration
NewContainerFromViper¶
Best for: Integration with existing Viper-based code.
// When you already have a configured Viper instance
v := viper.New()
v.SetConfigFile("legacy-config.yaml")
v.ReadInConfig()
container := config.NewContainerFromViper(l, v)
Behavior:
- Wraps existing Viper without modification
- Inherits all Viper settings (watchers, env bindings, etc.)
- Useful for gradual migration to GTB patterns
Decision Flowchart¶
flowchart TD
Start([Need Configuration]) --> Q1{Source type?}
Q1 -->|Files| Q2{Required?}
Q1 -->|io.Reader / bytes| Reader[NewReaderContainer]
Q1 -->|Existing Viper| Viper[NewContainerFromViper]
Q2 -->|Yes, fail if missing| Load[LoadFilesContainer]
Q2 -->|No, graceful fallback| Files[NewFilesContainer]
Load --> Done([Container Ready])
Files --> Done
Reader --> Done
Viper --> Done