Essential Libraries Basics
10 examples to get you started with Essential Libraries - 7 basic and 3 intermediate.
Search across all documentation pages
10 examples to get you started with Essential Libraries - 7 basic and 3 intermediate.
mkdir libdemo && cd libdemo && go mod init example.com/libdemo.go get go.uber.org/zap github.com/stretchr/testify/require github.com/spf13/viper github.com/go-chi/chi/v5.go run . after saving as main.go (or split files in the same module).Structured JSON logs with minimal setup.
package main
import (
"go.uber.org/zap"
)
func main() {
log, _ := zap.NewProduction()
defer log.Sync()
log.Info("service ready", zap.String("port", "8080"))
}NewProduction sets JSON output, sampling, and sensible defaults.defer log.Sync() flushes buffers on shutdown (ignore errors on some platforms).*zap.Logger into constructors instead of global state.Related: Logging: zap vs zerolog vs slog - when to pick zap over slog
No third-party logger required for structured fields.
package main
import (
"log/slog"
"os"
)
func main() {
h := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})
slog.SetDefault(slog.New(h))
slog.Info("ready", "service", "api")
}log/slog in stdlib.Related: Structured Logging with slog - handlers and migration
Readable test failures without custom helpers.
package user_test
import (
"testing"
"github.com/stretchr/testify/require"
)
func sum(a, b int) int { return a + b }
func TestSum(t *testing.T) {
require.Equal(t, 5, sum(2, 3))
}require stops the test on failure (use in setup preconditions).user_test.go; testify is test-only, not production code.tests slices for coverage.Related: Testing with testify & mockery - suites and mocks
Load YAML defaults with env override support.
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
_ = viper.ReadInConfig()
viper.SetDefault("port", 8080)
fmt.Println(viper.GetInt("port"))
}SetDefault applies when the key is missing from all sources.viper.AutomaticEnv() to let PORT override port keys.Related: Configuration with viper & envconfig - layered precedence
Stdlib-compatible HTTP routing.
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
)
func main() {
r := chi.NewRouter()
r.Get("/ping", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("pong"))
})
http.ListenAndServe(":8080", r)
}chi.NewRouter() returns an http.Handler.ResponseWriter and Request signatures.r.Use before registering routes.Related: chi: Lightweight Routing & Middleware - route groups
Named path segments without framework context types.
r.Get("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
w.Write([]byte("user " + id))
}){id} captures one segment; validate before database lookups.Map DATABASE_URL into a typed config field.
viper.SetEnvPrefix("APP")
viper.BindEnv("database_url")
viper.AutomaticEnv()
dsn := viper.GetString("database_url")
_ = dsnSetEnvPrefix namespaces env keys (APP_DATABASE_URL).main when required secrets are empty.SugaredLogger trades a little performance for ergonomics.
log, _ := zap.NewDevelopment()
defer log.Sync()
sugar := log.Sugar()
sugar.Infof("listening on %s", ":8080")log.Info("listening", zap.String("addr", addr)) in hot paths.*zap.Logger and call .Sugar() only at legacy boundaries.Group related tests with shared setup hooks.
import (
"testing"
"github.com/stretchr/testify/suite"
)
type UserSuite struct{ suite.Suite }
func (s *UserSuite) TestCreate() {
s.Equal(1, 1)
}
func TestUserSuite(t *testing.T) {
suite.Run(t, new(UserSuite))
}SetupTest / TearDownTest run around each test method.httptest servers.Related: testify, httptest & Test Doubles - doubles and httptest
Compose focused libraries at the HTTP edge.
import (
"net/http"
"time"
"github.com/go-chi/chi/v5/middleware"
"go.uber.org/zap"
)
func requestLog(log *zap.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
next.ServeHTTP(ww, r)
log.Info("request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.Int("status", ww.Status()),
zap.Duration("latency", time.Since(start)),
)
})
}
}WrapResponseWriter captures status codes for access logs.r.Context() into downstream calls for cancellation.Related: Prometheus & OpenTelemetry Client Libraries - metrics alongside logs
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 19, 2026