Skip to content

Configuration loading ​

pkg/config owns Env, LoadEnv, and legacy typed environment helpers. Startup calls LoadEnv once; it reads a default/explicit dotenv file, gives process environment precedence (even empty values), applies defaults for absent values, and returns malformed-file/numeric errors. It leaves process environment unchanged.

DATABASE_URL and JWT_SECRET default to empty and are checked by consumers. Identity validates JWT minimum secret length and maximum lifetimes; API startup verifies database connectivity. Configuration includes Redis values that are not yet used by startup.

GetEnv, GetEnvAsInt, and GetEnvAsBool are legacy helpers with different empty-value/fallback behavior. Prefer LoadEnv for application startup and explicit typed configuration for dependencies. See configuration settings and the tested example below.

go
package config_test

import (
	"fmt"
	"os"

	"github.com/prescriptoo/prescriptoo-api/pkg/config"
)

// LoadEnv accepts an explicit file, so tools can choose configuration without
// depending on the process working directory. Environment values take priority.
func ExampleLoadEnv() {
	// Fix values used by this example, restoring the caller's environment after it.
	for key, value := range map[string]string{
		"PORT": "9090", "JWT_EXPIRATION_HOURS": "1", "REFRESH_TOKEN_EXPIRATION_DAYS": "7",
	} {
		previous, exists := os.LookupEnv(key)
		defer func() {
			if exists {
				_ = os.Setenv(key, previous)
			} else {
				_ = os.Unsetenv(key)
			}
		}()
		if err := os.Setenv(key, value); err != nil {
			panic(err)
		}
	}
	file, err := os.CreateTemp("", "prescriptoo-config-*.env")
	if err != nil {
		panic(err)
	}
	defer func() {
		if err := os.Remove(file.Name()); err != nil {
			panic(err)
		}
	}()
	if _, err := file.WriteString("DATABASE_URL=postgres://localhost/example\nPORT=8088\n"); err != nil {
		panic(err)
	}
	if err := file.Close(); err != nil {
		panic(err)
	}
	env, err := config.LoadEnv(file.Name())
	if err != nil {
		panic(err)
	}
	fmt.Println("Port:", env.Port)
	fmt.Println("Access lifetime:", env.JWTExpirationHours)
	// Output:
	// Port: 9090
	// Access lifetime: 1
}

Tests cover precedence, explicitly empty values, missing default/explicit files, invalid numeric input, and lack of environment mutation.

Code reference ​

See the generated config package.

Documentation follows the source. Planned contracts are marked explicitly.