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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Struct tags map Go fields to YAML keys, and omitempty keeps zero-valued fields out of output.
- 2Seeding a struct before unmarshalling gives you defaults that YAML only overrides when present.
- 3Wrapping errors with %w preserves the cause while adding context about which operation failed.
Related explainers
java
public final class IbanValidator { private static final Map<String, Integer> COUNTRY_LENGTHS = Map.ofEntries( Map.entry("DE", 22), Map.entry("FR", 27), Map.entry("GB", 22),
Validating IBANs with the mod-97 checksum
validation
checksum
modular-arithmetic
Intermediate
8 steps
go
package tsvio import ( "bufio"
Reading and writing TSV records in Go
parsing
io-streams
error-handling
Intermediate
10 steps
go
package theme import ( "fmt"
Per-tenant HTML templates with Gin's renderer
multi-tenancy
concurrency
html-templates
Advanced
8 steps
php
<?php namespace App\Http\Controllers;
Streaming a filtered CSV export in Laravel
streaming
csv-export
query-builder
Intermediate
9 steps
go
package auth import ( "net/http"
Setting and reading secure session cookies in Go
cookies
session-management
security
Intermediate
6 steps
go
package middleware import ( "net/http"
A maintenance-mode gate in Gin
middleware
atomic-flag
concurrency
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/loading-and-saving-yaml-config-in-go-explained-go-a008/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.