go
55 lines · 8 steps
Loading typed config from environment variables in Go
A Load function fills a Config struct from environment variables, applying defaults and parsing each value into its proper type.
Explained by
highlit
1package config
2
3import (
4 "fmt"
5 "os"
6 "strconv"
7 "strings"
8 "time"
9)
10
11type Config struct {
12 Host string `env:"HOST"`
13 Port int `env:"PORT"`
14 Debug bool `env:"DEBUG"`
15 ReadTimeout time.Duration `env:"READ_TIMEOUT"`
16 AllowedHosts []string `env:"ALLOWED_HOSTS"`
17}
18
19func Load() (*Config, error) {
20 cfg := &Config{
21 Host: "0.0.0.0",
22 Port: 8080,
23 ReadTimeout: 15 * time.Second,
24 }
25
26 if v, ok := os.LookupEnv("HOST"); ok {
27 cfg.Host = v
28 }
29 if v, ok := os.LookupEnv("PORT"); ok {
30 port, err := strconv.Atoi(v)
31 if err != nil {
32 return nil, fmt.Errorf("PORT: %w", err)
33 }
34 cfg.Port = port
35 }
36 if v, ok := os.LookupEnv("DEBUG"); ok {
37 debug, err := strconv.ParseBool(v)
38 if err != nil {
39 return nil, fmt.Errorf("DEBUG: %w", err)
40 }
41 cfg.Debug = debug
42 }
43 if v, ok := os.LookupEnv("READ_TIMEOUT"); ok {
44 d, err := time.ParseDuration(v)
45 if err != nil {
46 return nil, fmt.Errorf("READ_TIMEOUT: %w", err)
47 }
48 cfg.ReadTimeout = d
49 }
50 if v, ok := os.LookupEnv("ALLOWED_HOSTS"); ok {
51 cfg.AllowedHosts = strings.Split(v, ",")
52 }
53
54 return cfg, nil
55}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Seed a config struct with sensible defaults, then let environment variables override only what's present.
- 2os.LookupEnv distinguishes an unset variable from an empty one, so absent vars keep their defaults untouched.
- 3Wrapping parse errors with the variable name turns a vague failure into an immediately actionable message.
Related explainers
go
package httpx import ( "net/http"
A retrying HTTP transport in Go
http
middleware
retry
Intermediate
8 steps
javascript
import { parsePhoneNumberFromString } from 'libphonenumber-js'; export class PhoneValidationError extends Error { constructor(message, code) {
Normalizing phone numbers with typed errors
validation
error-handling
custom-errors
Intermediate
6 steps
typescript
import { Injectable, computed, inject, signal } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { firstValueFrom } from 'rxjs';
Optimistic updates with Angular signals
signals
optimistic-updates
state-management
Intermediate
9 steps
python
import os from datetime import datetime, timezone import jwt
JWT bearer auth as a FastAPI dependency
authentication
jwt
dependency-injection
Intermediate
7 steps
rust
use std::error::Error; use std::path::Path; use serde::Deserialize;
Custom CSV deserialization with serde in Rust
serde
csv
deserialization
Intermediate
7 steps
python
import logging import traceback from flask import Blueprint, jsonify, request
Centralized JSON error handling in Flask
error-handling
blueprints
json-api
Intermediate
5 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-typed-config-from-environment-variables-in-go-explained-go-2493/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.