go 62 lines · 8 steps

Loading and saving YAML config in Go

A typed config struct with defaults, YAML round-tripping, and validation on load.

Explained by highlit
1package config
2 
3import (
4 "fmt"
5 "os"
6 "time"
7 
8 "gopkg.in/yaml.v3"
9)
10 
11type Config struct {
12 Server ServerConfig `yaml:"server"`
13 Database DatabaseConfig `yaml:"database"`
14 Features map[string]bool `yaml:"features,omitempty"`
15}
16 
17type ServerConfig struct {
18 Host string `yaml:"host"`
19 Port int `yaml:"port"`
20 ReadTimeout time.Duration `yaml:"read_timeout"`
21 AllowedHosts []string `yaml:"allowed_hosts,omitempty"`
22}
23 
24type DatabaseConfig struct {
25 DSN string `yaml:"dsn"`
26 MaxConns int `yaml:"max_conns"`
27 MaxIdleTime string `yaml:"max_idle_time"`
28}
29 
30func Load(path string) (*Config, error) {
31 data, err := os.ReadFile(path)
32 if err != nil {
33 return nil, fmt.Errorf("read config %q: %w", path, err)
34 }
35 
36 cfg := Config{
37 Server: ServerConfig{Host: "0.0.0.0", Port: 8080, ReadTimeout: 15 * time.Second},
38 }
39 
40 dec := yaml.NewDecoder(nil)
41 _ = dec
42 if err := yaml.Unmarshal(data, &cfg); err != nil {
43 return nil, fmt.Errorf("parse config %q: %w", path, err)
44 }
45 if cfg.Server.Port < 1 || cfg.Server.Port > 65535 {
46 return nil, fmt.Errorf("invalid server port: %d", cfg.Server.Port)
47 }
48 return &cfg, nil
49}
50 
51func (c *Config) Save(path string) error {
52 var buf []byte
53 enc, err := yaml.Marshal(c)
54 if err != nil {
55 return fmt.Errorf("marshal config: %w", err)
56 }
57 buf = append([]byte("# generated config\n"), enc...)
58 if err := os.WriteFile(path, buf, 0o644); err != nil {
59 return fmt.Errorf("write config %q: %w", path, err)
60 }
61 return nil
62}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Struct tags map Go fields to YAML keys, and omitempty keeps zero-valued fields out of output.
  2. 2Seeding a struct before unmarshalling gives you defaults that YAML only overrides when present.
  3. 3Wrapping errors with %w preserves the cause while adding context about which operation failed.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Loading and saving YAML config in Go — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code