# App Config in Go
The Node.js @app-config/cli
library has built-in support for generating go code.
Install it:
npm i --save-dev @app-config/cli@2
Add codegen instructions to the
.app-config.meta.yml
file:generate: - file: ./pkg/app-config.go
Run the code generation:
npx @app-config/cli gen
Use the config module!
port := GetConfig().Server.Port
This will result in a type-safe struct that represents your config faithfully. It also validates the config against the App Config schema.
Typically, users will wrap usage of their app in the @app-config/cli
CLI.
For example:
npx @app-config/cli -s -- go run .
The generated code will look something like this:
// @generated by app-config
package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"os"
"github.com/xeipuuv/gojsonschema"
)
var config Config
func init() {
loadedConfig, err := LoadConfig()
if err != nil {
log.Panic(err.Error())
}
config = loadedConfig
}
func GetConfig() Config {
return config
}
func LoadConfig() (Config, error) {
var loadedConfig Config
var loadedSchema map[string]interface{}
var err error
configText := os.Getenv("APP_CONFIG")
schemaText := os.Getenv("APP_CONFIG_SCHEMA")
if configText == "" {
return loadedConfig, errors.New("The APP_CONFIG environment variable was not set")
}
if schemaText == "" {
return loadedConfig, errors.New("The APP_CONFIG_SCHEMA environment variable was not set")
}
err = json.Unmarshal([]byte(schemaText), &loadedSchema)
if err != nil {
return loadedConfig, fmt.Errorf("Could not parse APP_CONFIG_SCHEMA environment variable: %s", err.Error())
}
err = json.Unmarshal([]byte(configText), &loadedConfig)
if err != nil {
return loadedConfig, fmt.Errorf("Could not parse APP_CONFIG environment variable: %s", err.Error())
}
schemaLoader := gojsonschema.NewGoLoader(loadedSchema)
documentLoader := gojsonschema.NewGoLoader(loadedConfig)
result, err := gojsonschema.Validate(schemaLoader, documentLoader)
if err != nil {
return loadedConfig, fmt.Errorf("Could not validate App Config: %s", err.Error())
}
if !result.Valid() {
errors := ""
for _, desc := range result.Errors() {
if errors == "" {
errors = fmt.Sprintf("%v", desc)
} else {
errors = fmt.Sprintf("%s, %v", errors, desc)
}
}
return loadedConfig, fmt.Errorf("The App Config value invalid: %s", errors)
}
return loadedConfig, nil
}
func UnmarshalConfig(data []byte) (Config, error) {
var r Config
err := json.Unmarshal(data, &r)
return r, err
}
func (r *Config) Marshal() ([]byte, error) {
return json.Marshal(r)
}
type Config struct {
Jwt Jwt `json:"jwt"`
Server Server `json:"server"`
}
type Jwt struct {
Secret string `json:"secret"`
}
type Server struct {
Port int64 `json:"port"`
}
You can specify the "no-singleton" option to avoid automatic (init
function) config loading:
generate:
- file: ./pkg/app-config.go
rendererOptions:
no-singleton: 'true'
Note that code generation is not guaranteed to be deterministic between versions of app config.
We'll do our best to be stable though, and output code that gofmt
is happy with.