Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Configuration

maniflex.Config is the single struct passed to maniflex.New. Every field has a sensible default; populate only the ones that differ from those defaults.

server := maniflex.New(maniflex.Config{
    Port:        8080,
    PathPrefix:  "/api",
})

Server

FieldDefaultPurpose
Port8080TCP port the HTTP server binds to
PathPrefix/apiURL prefix prepended to every generated model route and /openapi.json
ServiceName""service identifier added to logs, audit records, and the X-Service-Name response header
StaticDir""filesystem directory served as static files; empty serves nothing (opt-in). Relative paths resolve against cwd
StaticPrefix/staticURL prefix the static directory is mounted under, at the router root
StaticDisabledfalseturn static file serving off even when StaticDir is set

PathPrefix does not affect /static, /files, or /health. Those are mounted at the router root. See Static Files for the static serving options.

HTTP timeouts

The framework owns the http.Server, and net/http gives that struct no deadlines by default. These are the ones it sets on your behalf:

FieldDefaultPurpose
ReadHeaderTimeout10show long a connection may take to send its request headers
IdleTimeout120show long a keep-alive connection may sit idle between requests
ReadTimeout0 (unbounded)time to read an entire request, headers and body
WriteTimeout0 (unbounded)time to write a response

ReadHeaderTimeout is the slowloris defence. Without it a client can hold a connection open forever by dribbling one header byte at a time, and enough such connections exhaust the server’s file descriptors without a single request ever reaching the pipeline. Set a negative value to disable a timeout (which is what net/http reads as “no deadline”) — only sensible behind a proxy that already bounds header reads.

ReadTimeout and WriteTimeout are deliberately left unset, because both are whole-request deadlines rather than idle deadlines:

  • A ReadTimeout caps how long a client may take to upload, so a large file over a slow link is severed mid-transfer.
  • A WriteTimeout covers the entire response, so any value at all would cut a long-lived stream — realtime.SSEHandler, a large download — off at that mark.

Set them when you know your request sizes and have no streaming endpoints. The header phase stays bounded by ReadHeaderTimeout either way.

Limits

FieldDefaultPurpose
MaxConcurrentExports4how many GET /:model/export requests may run at once, server-wide; negative disables the limit

An export holds its entire result set in memory until the last byte reaches the client, so concurrency multiplies the largest allocation the server makes. The per-model MaxExportRows bounds one export’s rows but not the row width nor the number in flight; this bounds the product. Requests over the limit are refused immediately with 503 EXPORT_BUSY and a Retry-After, not queued. See CSV / XLSX Export.

Database

FieldDefaultPurpose
DBnilthe default DBAdapter. Usually set via server.SetDB(db) after MustRegister. Optional when every model has its own ModelConfig.Adapter — see Per-model adapter routing
DisableAutoMigratefalseskip schema migration on startup (migration runs by default)
DBWriteURL""DSN for the primary database (informational; populated by ConfigFromEnv)
DBReadURL""DSN for the read replica (informational)
QueryTimeout0 (unlimited)per-request deadline applied to all DB calls; exceeding it produces 504 TIMEOUT

See Database Backends for adapter construction.

File storage and encryption

FieldPurpose
FilesConfig.Storagemaniflex.FileStorage implementation for mfx:"file" fields and the /files endpoints. Required if any model uses file uploads. See File Fields & Uploads.
FilesConfig.BeforeMiddlewares[]maniflex.MiddlewareFunc wrapping the standalone /files endpoints. Empty = no auth (backward-compatible default); production deployments should populate this with at least an auth middleware. See File Fields & Uploads.
KeyProvidermaniflex.KeyProvider for mfx:"encrypted" fields. Without one, encrypted fields refuse writes with 500 ENCRYPTION_NOT_CONFIGURED.

Logging

FieldDefaultPurpose
Loggerslog.Default()logger used for lifecycle, per-request, and adapter messages
PanicLoggerfalls back to Loggersink for the panic recoverer’s structured panic records
Tracezero (off)pipeline tracing — see below

Logger is used by ctx.Logger(), which adds request_id, trace_id, and service attributes per request. Route it to a JSON handler in production.

Pipeline tracing

Config.Trace enables verbose debug output of the request pipeline. All trace output is at DEBUG level through Logger, so the handler must accept DEBUG records to see anything.

Sub-flagEffect
Enabledshorthand for Steps + Timings + Aborts
Stepsenter/exit record per middleware
Timingsper-middleware elapsed time on exit records
Abortsthe source file:line of every ctx.Abort call
Bodieslog field names present in ctx.ParsedBody (opt-in; may expose sensitive field names)
Skipslog middleware skipped by ForModel/ForOperation filters
cfg.Trace = maniflex.PipelineTrace{Enabled: true, Skips: true}

Enabled expands into the three standard flags unless one of those three is already set, in which case it stays out of the way and you get exactly what you named. Bodies and Skips are additive: setting one does not suppress the expansion, so the example above gives all four.

cfg.Trace = maniflex.PipelineTrace{Enabled: true, Steps: true}  // Steps only
cfg.Trace = maniflex.PipelineTrace{Bodies: true}                // Bodies only

Leave Bodies off in production.

Lifecycle

FieldDefaultPurpose
ShutdownTimeout30smaximum time Start() waits for in-flight requests to finish on SIGINT / SIGTERM before forcing the listener closed

See Graceful Shutdown.

Health probe

FieldDefaultPurpose
HealthCheckDBfalsewhen true, GET /health pings every distinct registered adapter (Config.DB plus any per-model overrides) and returns 503 on failure. Driver error messages are logged, not echoed in the response body, so DSN fragments can’t leak.
HealthTimeout3smaximum time the health handler waits for the DB ping

Set HealthTimeout shorter than your probe’s timeoutSeconds so the handler can return 503 cleanly before the probe times out.

Reading from environment

maniflex.ConfigFromEnv(prefix) populates a Config from a conventional set of environment variables. Use it for twelve-factor deployments, then override individual fields in code where needed.

cfg, err := maniflex.ConfigFromEnv("")   // or "ORDERS" → ORDERS_PORT, ORDERS_DB_WRITE_URL, …
if err != nil {
    log.Fatal(err)
}
cfg.DisableAutoMigrate = true  // disable for production
server := maniflex.New(cfg)

These are the variables it reads, and the only ones — anything else on Config is set in code:

VariableFieldValue
PORTPortinteger, 1–65535
DB_WRITE_URLDBWriteURLstring
DB_READ_URLDBReadURLstring
QUERY_TIMEOUT_MSQueryTimeoutpositive integer, milliseconds
SHUTDOWN_TIMEOUT_SShutdownTimeoutpositive integer, seconds
SERVICE_NAMEServiceNamestring
HEALTH_CHECK_DBHealthCheckDBtrue/false, 1/0, yes/no, on/off

A variable that is unset leaves its field at the zero value, for ApplyDefaults to fill in. A variable that is set but unreadable is an error — PORT=808O, QUERY_TIMEOUT_MS=abc, HEALTH_CHECK_DB=ture — naming the variable and the value it could not read. Every bad variable is reported at once, so two typos take one deploy to find rather than two. Don’t discard this error: a mistyped PORT that is quietly ignored gives you a healthy-looking server listening on 8080, and nothing anywhere says why.