Config
GoLobby Config is a lightweight yet powerful config package for Go projects. It takes advantage of env files and OS variables alongside config files to be your ultimate requirement.
Documentation
Required Go Version
It requires Go v1.11
or newer versions.
Installation
To install this package run the following command in the root of your project
go get github.com/golobby/config
A simple example
The following example demonstrates how to set and get a simple key/value.
c, err := config.New()
// Check error...
c.Set("name", "John Doe")
name, err := c.Get("name")
Feeders
Feeders provide content of the configuration. Currently, these feeders exist out of the box:
Map
: Feeds a simplemap[string]interface{}
.Json
: Feeds a JSON file.JsonDirectory
: Feeds a directory of JSON files.Yaml
: Feeds a Yaml file.
Of course, you are free to implement your feeders by implementing the Feeder
interface.
You can pass your desired feeder through Options to the New()
function this way:
c, err := config.New(config.Options{
Feeder: TheFeederGoesHere,
})
Feeding using Map feeder
You don't like config files!? It's OK you can pass a Map
feeder to the Config initializer like this example:
c, err := config.New(config.Options{
Feeder: feeder.Map{
"name": "Hey You",
"band": "Pink Floyd",
"year": 1979,
"duration": 4.6,
},
})
if err != nil {
panic(err)
}
name, err := c.Get("name")
// OR
name, err := c.GetString("name")
year, err := c.Get("year")
// OR
year, err := c.GetInt("year")
year, err := c.Get("duration")
// OR
duration, err := c.GetFloat("duration")
Feeding using Json feeder
Storing configuration data in a JSON file could be a brilliant idea. The example below shows how to use the Json feeder.
config.json
:
{
"name": "MyAppUsingGoLobbyConfig",
"version": 3.14,
"numbers": [
1,
2,
3
],
"users": [
{
"name": "Milad Rahimi",
"year": 1993,
"address": {
"country": "Iran",
"state": "Lorestan",
"city": "Delfan"
}
},
{
"name": "Amirreza Askarpour",
"year": 1998,
"address": {
"country": "Iran",
"state": "Khouzestan",
"city": "Ahvaz"
}
}
]
}
Go code:
c, err := config.New(config.Options{
Feeder: feeder.Json{Path: "path/to/config.json"},
})
v, err := c.Get("version") // 3.14
v, err := c.Get("numbers.2") // 3
v, err := c.Get("users.0.address.city") // Delfan
Feeding using Yaml feeder
Yaml files are a trend these days, so why not store configurations in them ?
config.yaml
:
name: MyAppUsingGoLobbyConfig
version: 3.14
numbers:
- 1
- 2
- 3
users:
- name: Milad Rahimi
year: 1993
address:
country: Iran
state: Lorestan
city: Delfan
- name: Amirreza Askarpour
year: 1998
address:
country: Iran
state: Khouzestan
city: Ahvaz
Go code:
c, err := config.New(config.Options{
Feeder: feeder.Yaml{Path: "path/to/config.yaml"},
})
v, err := c.Get("version") // 3.14
v, err := c.Get("numbers.2") // 3
v, err := c.Get("users.0.address.city") // Delfan
Feeding using JsonDirectory
If you have many configuration data and it doesn't fit in a single JSON file. In this case, you can use multiple JSON files and feed them using JsonDirectory feeder like this example:
Sample project directory structure:
- main.go
- config
- - app.json
- - db.json
app.json
:
{
"name": "MyApp",
"version": 3.14
}
db.json
:
{
"sqlite": { "path": "app.db" },
"mysql": { "host": "localhost", "user": "root", "pass": "secret" }
}
Go code:
c, err := config.New(config.Options{
Feeder: feeder.JsonDirectory{Path: "config"},
})
v, err := c.Get("app.version") // 3.14
v, err := c.Get("db.mysql.host") // localhost
Feeding using YamlDirectory
If you have many configuration data and it doesn't fit in a single YAML file. In this case, you can use multiple YAML files and feed them using YamlDirectory feeder like this example:
Sample project directory structure:
- main.go
- config
- - app.yaml
- - db.yaml
app.yaml
:
name: MyApp
version: 3.14
db.yaml
:
sqlite:
path: app.db
mysql:
host: localhost
user: root
pass: secret
Go code:
c, err := config.New(config.Options{
Feeder: feeder.YamlDirectory{Path: "config"},
})
v, err := c.Get("app.version") // 3.14
v, err := c.Get("db.mysql.host") // localhost
OS variables and environment files
OS variables
Sometimes you need to use environment variables stored in OS alongside your configuration data. You can refer to OS variables using a simple syntax, ${ VARIABLE }
, in the config values. This example demonstrates how to use OS variables.
db.json
:
{"name": "MyApp", "port": "${ APP_PORT }"}
Go code:
c, err := config.New(config.Options{
Feeder: feeder.Json{Path: "db.json"},
})
v, err := c.Get("name") // MyApp
v, err := c.Get("port") // equivalent to os.Getenv("APP_PORT")
If you need to have a default value in case of lacking OS variable you can use this syntax:
${ VARIABLE | DEFAULT }
Example of JSON file using OS variable:
{"name": "MyApp", "port": "${ APP_PORT | 3306 }"}
environment files
You maybe want to use ".env" files. Good news! It's so easy to work with environment files. You can pass an environment file path alongside your config feeder when you initialize a new instance of Config.
Sample project directory structure:
- main.go
- .env
- config.json
config.json
:
{"name": "MyApp", "key": "${ APP_KEY }", "port": "${ APP_PORT | 3306 }"}
.env
:
APP_KEY=secret
APP_PORT=
Go code:
c, err := config.New(config.Options{
Feeder: feeder.Json{Path: "config.json"},
Env: ".env",
})
v, err := c.Get("name") // MyApp (from config.json)
v, err := c.Get("key") // secret (from .env)
v, err := c.Get("port") // 3306 (from config.json, the default value)
Reload the config and env files
One of the benefits of using config management tools is the ability to change the configurations without redeployment. The Config package takes advantage of OS signals to handle this need. It listens to the "SIGHUP" signal and reloads the env and config files on receive.
You can send this signal to your application with following shell command:
KILL -SIGHUP [YOUR-APP-PROCESS-ID]
To get your application process id you can use ps
shell command.
Altogether!
In this section, we illustrate a complete example that shows many of the package features.
Sample project directory structure:
- main.go
- .env
- config
- - app.json
- - db.json
app.json
:
{
"name": "MyApp",
"key": "${ APP_KEY }"
}
db.json
:
{
"sqlite": {
"path": "app.db"
},
"mysql": {
"host": "${ DB_HOST | localhost }",
"user": "${ DB_USER | root }",
"pass": "${ DB_PASS | secret }"
}
}
.env
:
APP_KEY=theKey
DB_HOST=127.0.0.1
DB_USER=
DB_PASS=something
Go code:
_ := os.Setenv("DB_HOST", "192.168.0.13")
c, err := config.New(config.Options{
Feeder: feeder.JsonDirectory{Path: "config"},
Env: ".env",
})
v, err := c.Get("app.name") // MyApp (from app.json)
v, err := c.Get("app.key") // theKey (from .env)
v, err := c.Get("db.mysql.host") // 192.168.0.13 (from OS variables)
v, err := c.Get("db.mysql.user") // root (from app.json, the default value)
v, err := c.Get("db.mysql.pass") // something (from .env)
You may ask what would happen if the value existed in the config file, the environment file, and the OS variables? It's the order of the Config priorities:
- OS Variables
- Environment (.env) files
- Default Value
So if the value was defined is OS variables, the Config would return it. If it wasn't in OS variables, the Config would return the value stored in the environment file. If it also wasn't in the environment file, it'd eventually return the value stored in the config file as default value.
License
GoLobby Config is released under the MIT License.