Quidest?

Cobra and Viper Quickstart

Cobra & Viper Quickstart

Cobra

Cobra is a library for building CLI applications with commands, subcommands, and flags.

Install

1go get -u github.com/spf13/cobra/cobra

Structure

A typical Cobra project separates each command into its own file under a cmd/ directory.

cmd/
  root.go    # root command
  serve.go   # subcommand
main.go

main.go

The entrypoint simply calls into the cmd package. All CLI logic lives there.

1package main
2
3import "myapp/cmd"
4
5func main() {
6    cmd.Execute()
7}

cmd/root.go

Define the root command as a package-level variable. Use is the command name, Short is the help text, and Run contains the logic. Execute() is the public entrypoint that parses args and runs the appropriate command.

 1package cmd
 2
 3import (
 4    "fmt"
 5    "os"
 6    "github.com/spf13/cobra"
 7)
 8
 9var rootCmd = &cobra.Command{
10    Use:   "myapp",
11    Short: "Short description",
12    Run: func(cmd *cobra.Command, args []string) {
13        fmt.Println("hello")
14    },
15}
16
17func Execute() {
18    if err := rootCmd.Execute(); err != nil {
19        os.Exit(1)
20    }
21}

Adding a subcommand (cmd/serve.go)

Subcommands are defined similarly. Use init() to register flags and attach the subcommand to its parent. IntVarP binds the flag to a variable — the P variant adds a short flag (-p).

 1package cmd
 2
 3import "github.com/spf13/cobra"
 4
 5var port int
 6
 7var serveCmd = &cobra.Command{
 8    Use:   "serve",
 9    Short: "Start server",
10    Run: func(cmd *cobra.Command, args []string) {
11        // use port here
12    },
13}
14
15func init() {
16    serveCmd.Flags().IntVarP(&port, "port", "p", 8080, "port number")
17    rootCmd.AddCommand(serveCmd)
18}

Usage

1myapp              # runs root
2myapp serve -p 3000  # runs subcommand with flag

Key concepts


Viper

Viper handles configuration from files, environment variables, and flags with a unified API.

Install

1go get -u github.com/spf13/viper

Basic usage

Set defaults first, then tell Viper where to look for config files. AddConfigPath can be called multiple times to search multiple directories. Use typed getters to retrieve values.

 1import "github.com/spf13/viper"
 2
 3func init() {
 4    // Set defaults
 5    viper.SetDefault("port", 8080)
 6    viper.SetDefault("debug", false)
 7
 8    // Config file
 9    viper.SetConfigName("config")       // config.yaml, config.json, etc.
10    viper.SetConfigType("yaml")         // explicit format
11    viper.AddConfigPath(".")            // look in current dir
12    viper.AddConfigPath("$HOME/.myapp") // then home dir
13
14    if err := viper.ReadInConfig(); err != nil {
15        // handle missing config (often optional)
16    }
17}
18
19func main() {
20    port := viper.GetInt("port")
21    debug := viper.GetBool("debug")
22    name := viper.GetString("name")
23}

Example config.yaml

Viper supports YAML, JSON, TOML, and other formats. Nested structures are supported.

1port: 3000
2debug: true
3name: myapp
4database:
5  host: localhost
6  port: 5432

Nested keys

Access nested values using dot notation.

1viper.GetString("database.host")  // "localhost"

Environment variables

AutomaticEnv makes Viper check for env vars matching config keys. SetEnvPrefix adds a prefix to avoid collisions. The replacer maps nested keys (dots) to underscores for env var names.

1viper.AutomaticEnv()                                    // reads MYAPP_PORT, etc.
2viper.SetEnvPrefix("MYAPP")                             // prefix for env vars
3viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))  // database.host → DATABASE_HOST

Cobra integration

Bind Cobra flags to Viper keys with BindPFlag. This unifies CLI flags, env vars, and config files under one key — Viper resolves them in priority order.

1var rootCmd = &cobra.Command{...}
2
3func init() {
4    rootCmd.Flags().IntP("port", "p", 8080, "port")
5    viper.BindPFlag("port", rootCmd.Flags().Lookup("port"))
6}

Priority: flag > env > config > default

Unmarshal to struct

Map the entire config to a struct. Use mapstructure tags to match keys to fields.

1type Config struct {
2    Port  int      `mapstructure:"port"`
3    Debug bool     `mapstructure:"debug"`
4    DB    DBConfig `mapstructure:"database"`
5}
6
7var cfg Config
8viper.Unmarshal(&cfg)

Watch for changes

Viper can watch the config file and trigger a callback on changes. Useful for live-reloading configuration.

1viper.WatchConfig()
2viper.OnConfigChange(func(e fsnotify.Event) {
3    fmt.Println("config changed:", e.Name)
4})

#computer-science #programming #golang