# 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@2Add codegen instructions to the
.app-config.meta.ymlfile:generate: - file: ./pkg/app-config.goRun the code generation:
npx @app-config/cli genUse 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.