1# ff [](https://pkg.go.dev/github.com/peterbourgon/ff/v3) [](https://github.com/peterbourgon/ff/releases/latest) [](https://github.com/peterbourgon/ff/actions?query=workflow%3ATest)
2
3ff stands for flags-first, and provides an opinionated way to populate a
4[flag.FlagSet](https://golang.org/pkg/flag#FlagSet) with configuration data from
5the environment. By default, it parses only from the command line, but you can
6enable parsing from environment variables (lower priority) and/or a
7configuration file (lowest priority).
8
9Building a commandline application in the style of `kubectl` or `docker`?
10Consider [package ffcli](https://pkg.go.dev/github.com/peterbourgon/ff/v3/ffcli),
11a natural companion to, and extension of, package ff.
12
13## Usage
14
15Define a flag.FlagSet in your func main.
16
17```go
18import (
19 "flag"
20 "os"
21 "time"
22
23 "github.com/peterbourgon/ff/v3"
24)
25
26func main() {
27 fs := flag.NewFlagSet("my-program", flag.ExitOnError)
28 var (
29 listenAddr = fs.String("listen-addr", "localhost:8080", "listen address")
30 refresh = fs.Duration("refresh", 15*time.Second, "refresh interval")
31 debug = fs.Bool("debug", false, "log debug information")
32 _ = fs.String("config", "", "config file (optional)")
33 )
34```
35
36Then, call ff.Parse instead of fs.Parse.
37[Options](https://pkg.go.dev/github.com/peterbourgon/ff/v3#Option)
38are available to control parse behavior.
39
40```go
41 ff.Parse(fs, os.Args[1:],
42 ff.WithEnvVarPrefix("MY_PROGRAM"),
43 ff.WithConfigFileFlag("config"),
44 ff.WithConfigFileParser(ff.PlainParser),
45 )
46```
47
48This example will parse flags from the commandline args, just like regular
49package flag, with the highest priority. (The flag's default value will be used
50only if the flag remains unset after parsing all provided sources of
51configuration.)
52
53Additionally, the example will look in the environment for variables with a
54`MY_PROGRAM` prefix. Flag names are capitalized, and separator characters are
55converted to underscores. In this case, for example, `MY_PROGRAM_LISTEN_ADDR`
56would match to `listen-addr`.
57
58Finally, if a `-config` file is specified, the example will try to parse it
59using the PlainParser, which expects files in this format.
60
61
62```
63listen-addr localhost:8080
64refresh 30s
65debug true
66```
67
68You could also use the JSONParser, which expects a JSON object.
69
70```json
71{
72 "listen-addr": "localhost:8080",
73 "refresh": "30s",
74 "debug": true
75}
76```
77
78Or, you could write your own config file parser.
79
80```go
81// ConfigFileParser interprets the config file represented by the reader
82// and calls the set function for each parsed flag pair.
83type ConfigFileParser func(r io.Reader, set func(name, value string) error) error
84```
85
86## Flags and env vars
87
88One common use case is to allow configuration from both flags and env vars.
89
90```go
91package main
92
93import (
94 "flag"
95 "fmt"
96 "os"
97
98 "github.com/peterbourgon/ff/v3"
99)
100
101func main() {
102 fs := flag.NewFlagSet("myservice", flag.ExitOnError)
103 var (
104 port = fs.Int("port", 8080, "listen port for server (also via PORT)")
105 debug = fs.Bool("debug", false, "log debug information (also via DEBUG)")
106 )
107 ff.Parse(fs, os.Args[1:], ff.WithEnvVarNoPrefix())
108
109 fmt.Printf("port %d, debug %v\n", *port, *debug)
110}
111```
112
113```
114$ env PORT=9090 myservice
115port 9090, debug false
116$ env PORT=9090 DEBUG=1 myservice -port=1234
117port 1234, debug true
118```
View as plain text