Skip to content

Configuration

wiki edited this page Sep 4, 2026 · 1 revision

Configuration

type Config struct {
	DefaultRouter   RouterConfig
	ShutdownTimeout time.Duration // default 10s
}
app := rex.New(rex.WithConfig(&rex.Config{
	DefaultRouter: rex.RouterConfig{
		Addr:    ":8080",
		BaseURL: "/api",
	},
	ShutdownTimeout: 30 * time.Second,
}))

rex.NewDefaultConfig() returns the defaults, if you would rather adjust than build.

Configuration may be applied at any point before Run — it is frozen as the first step of Run, so ordering against routes, routers and middleware does not matter. Applying it after Run has no effect and is not reported: Option is func(r Rex) with no error return, so "erroring" could only have meant a panic or a log line nobody reads.

RouterConfig

One per listener. The default router takes Config.DefaultRouter; every other router takes the one passed to CreateRouter.

type RouterConfig struct {
	Addr      string      // ":8080"
	BaseURL   string      // "/"
	ListenSSL bool        // false — TLS is opt-in
	CertFile  *string
	KeyFile   *string
	TLSConfig *tls.Config // takes precedence; also where mTLS lives

	ReadHeaderTimeout   time.Duration // 10s  — the Slowloris bound
	ReadTimeout         time.Duration // 30s
	WriteTimeout        time.Duration // 0    — unset, deliberately
	IdleTimeout         time.Duration // 120s
	MaxHeaderBytes      int           // 1 MiB
	MaxHeaderValueCount int           // 0 → net/http's own 500
	MaxBodyBytes        int64         // 4 MiB
}

Zero takes the default; negative disables. Full explanation of each limit and of TLS resolution: TLS and Listener Limits.

TLS is off by default

ListenSSL defaults to false, and both certificate paths default to nil. It previously defaulted to true while the paths defaulted to nil, so an unmodified default config asked for TLS with no certificate anywhere — which Start rejects. Defaulting to false makes the out-of-the-box config start, and makes enabling TLS a deliberate act.

Older documentation says ListenSSL defaults to true. It does not.

There are no struct tags

RouterConfig used to carry default:"…" tags that nothing read. They were documentation pretending to be behaviour, and they had already drifted: SSLVerify was tagged default:"true" while having no effect at all, and ListenSSL was tagged default:"true" while defaulting to false.

Defaults now live in NewDefaultConfig and in the router's own resolution helpers, and are stated in each field's doc comment. One place to read, one place to change.

ShutdownTimeout

Bounds graceful shutdown: how long OnStop and OnShutdown hooks have, and how long a Stop() arriving mid-startup waits for startup to finish before proceeding anyway.

10 seconds by default. Raise it if your extensions drain queues; do not raise it past your orchestrator's own kill timeout, or the process is killed mid-drain regardless.

Options

Option
rex.WithConfig(*Config) replace the whole configuration
rex.WithLogger(logger.Logger) set the logger
rex.WithLogLevel(logger.LogLevel) set the level, before or after a logger
rex.WithExtension(ext) register one extension
rex.WithExtensions(ext…) register several

Extensions supply their own, returning the same Option type:

app := rex.New(
	rex.WithConfig(cfg),
	cors.WithCORS(corsCfg),
	health.WithHealth(nil),
)

Option is rextension.Option, so an extension returns one without importing rex.

Reading it back

cfg := app.Config() // pending before Run, effective after

Configuration from the environment

rex does not read the environment, parse flags or load files — that is the application's job, and doing it in main keeps the failure where it can be returned:

func loadConfig() (*rex.Config, error) {
	addr := os.Getenv("ADDR")
	if addr == "" {
		addr = ":8080"
	}
	timeout, err := time.ParseDuration(cmp.Or(os.Getenv("SHUTDOWN_TIMEOUT"), "10s"))
	if err != nil {
		return nil, fmt.Errorf("SHUTDOWN_TIMEOUT: %w", err)
	}
	return &rex.Config{
		DefaultRouter:   rex.RouterConfig{Addr: addr},
		ShutdownTimeout: timeout,
	}, nil
}

Clone this wiki locally