Skip to main content
Version: 1.7.0

Configuration Reference

Quark configuration is per client. quark.New(driverName, dataSource, ...options) does not modify global ORM state.

Minimal Client

client, err := quark.New("sqlite", "file:app.db?cache=shared")

quark.New opens the connection itself and pings it with a five-second startup timeout. The dialect is auto-detected from the driver name.

Production Shape

limits := quark.DefaultLimits()
limits.MaxResults = 10_000
limits.MaxWhereConditions = 20
limits.MaxJoins = 5
limits.QueryTimeout = 30 * time.Second
limits.AllowRawQueries = false
limits.SafeMigrations = true

store := memory.New()

client, err := quark.New("postgres", dsn,
quark.WithLogger(logger),
quark.WithLimits(limits),
quark.WithCacheStore(store),
quark.WithMiddleware(quarkotel.New()),
quark.WithQueryObserver(metricsObserver),
)

Always start from DefaultLimits() and override fields selectively, as above. A bare literal like quark.Limits{MaxResults: 500} silently leaves SafeMigrations at false, which allows a destructive Sync. The full zero-value rules are under Limits below.

Options

OptionPurpose
WithDialect(d Dialect)Overrides the dialect that would be auto-detected from the driver name.
WithLogger(l *slog.Logger)Sets the client logger.
WithLimits(l Limits)Sets security and performance limits.
WithCacheStore(s CacheStore)Enables query cache storage.
WithMiddleware(m Middleware)Adds execution middleware.
WithQueryObserver(o QueryObserver)Adds post-execution query observers.
WithDeadlockRetry(maxAttempts int)Transparent retry of Client.Tx when the engine kills the transaction as a deadlock victim (PG 40P01, MySQL 1213, MSSQL 1205, Oracle ORA-00060). Retries the whole closure with backoff + jitter; disabled by default.
WithSlowQueryThreshold(d time.Duration)Logs any query/exec exceeding d at WARN with structured attributes (parameterised SQL only — never bind args). 0 (default) disables.
WithStrictReads(mode StrictReadsMode)Enforcement for unbounded reads: StrictReadsWarn logs a structured WARN when Iter/Cursor run without Limit; StrictReadsReject returns ErrInvalidQuery instead. Enables N+1 detection inside TrackReads contexts. Per-query opt-out: AllowUnbounded(). Off by default.
WithDefaultTZ(loc *time.Location)Fallback timezone for time.Time columns without a quark:"tz=..." tag. Values are stored as UTC; loc only affects how scanned values read in Go.
WithCacheJitter(pct float64)±jitter factor applied to cache TTLs (anti-stampede). Default 0.1; clamped to [0, 1]. Needs WithCacheStore.
WithCacheXFetchBeta(beta float64)XFetch probabilistic early-refresh parameter. Default 1.0; 0 disables XFetch (singleflight + jitter stay on). Needs WithCacheStore.
WithCacheCrossInstance()Cross-instance cache-stampede coordination via a CacheLocker store. Off by default; falls back to in-process singleflight when the store can't lock.
WithReplicas(dsns ...string)Read-replica DSNs: reads route to replicas, writes stay on the primary; transient replica failures fail over to the primary.
WithReplicaStrategy(s ReplicaStrategy)Replica selection: ReplicaRoundRobin (default), ReplicaRandom, or ReplicaLeastConn. Needs WithReplicas.
WithReplicaDownCooldown(d time.Duration)How long a failed replica stays out of rotation before re-probing (default 5s). Needs WithReplicas.
WithMaxOpenConns(n int)Sets *sql.DB.SetMaxOpenConns.
WithMaxIdleConns(n int)Sets *sql.DB.SetMaxIdleConns.
WithConnMaxLifetime(d time.Duration)Sets *sql.DB.SetConnMaxLifetime.
WithConnMaxIdleTime(d time.Duration)Sets *sql.DB.SetConnMaxIdleTime.

Dialect

quark.WithDialect(quark.PostgreSQL())
quark.WithDialect(quark.MySQL())
quark.WithDialect(quark.MariaDB())
quark.WithDialect(quark.SQLite())
quark.WithDialect(quark.MSSQL())
quark.WithDialect(quark.Oracle())

The dialect controls placeholders, identifier quoting, RETURNING, last-insert ID behavior, upsert SQL, JSON extraction, pagination, routines, and schema DDL.

quark.New auto-detects the dialect from the driver name. Pass WithDialect only when the driver name is ambiguous (e.g. pgx shared between PostgreSQL and CockroachDB) or when registering a custom dialect.

Logger

logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

client, err := quark.New("postgres", dsn,
quark.WithLogger(logger),
)

The logger is used for client initialization, safe default limit warnings, schema sync messages, cache hits, and migration diagnostics.

Limits

type Limits struct {
MaxQueryLength int
MaxResults int
MaxJoins int
MaxWhereConditions int
QueryTimeout time.Duration
AllowRawQueries bool
SafeMigrations bool
}

Defaults:

FieldDefaultNotes
MaxQueryLength10 * 1024Rejects generated SELECT SQL above this byte length.
MaxResults10000Intended cap for list-style reads. Prefer explicit Limit.
MaxJoins5Rejects SELECT queries with too many joins.
MaxWhereConditions20Rejects excessive predicate chains.
QueryTimeout30 * time.SecondApplied to ORM query contexts.
AllowRawQueriesfalseRequired for RawQuery, Exec, and WhereSubquery.
SafeMigrationstruePrevents Sync from dropping columns.

Zero-value semantics

WithLimits treats numeric and boolean fields differently, and the difference matters:

  • Numeric fields: zero means "not set". WithLimits replaces each zero-valued numeric field with its DefaultLimits() value before installing the struct, so quark.Limits{MaxResults: 500} raises one limit without zeroing the rest.
  • Negative values pass through untouched. MaxQueryLength: -1 disables the generated-SQL length check.
  • Boolean fields are never rewritten, because an explicit false is indistinguishable from an omitted one. A partial literal therefore carries SafeMigrations: false — destructive Sync allowed — even though the default is true.

Because that last case is easy to miss, quark.New logs one warning (event=quark.limits.partial_literal_safe_migrations_off) when a partial literal ends up with SafeMigrations disabled. The warning does not change the installed limits; a full literal that disables SafeMigrations on purpose stays silent.

Recommended pattern:

limits := quark.DefaultLimits()
limits.AllowRawQueries = true // only for migration/admin clients
limits.MaxWhereConditions = 30

client, err := quark.New("postgres", dsn,
quark.WithLimits(limits),
)

Use separate app and migration clients when possible: app clients can keep AllowRawQueries disabled, while migration commands can opt in explicitly.

Cache Store

Memory:

import "github.com/jcsvwinston/quark/cache/memory"

store := memory.New()
defer store.Close()

client, err := quark.New("sqlite", "file:app.db?cache=shared",
quark.WithCacheStore(store),
)

Redis:

import rediscache "github.com/jcsvwinston/quark/cache/redis"

store := rediscache.New(rediscache.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})

client, err := quark.New("postgres", dsn,
quark.WithCacheStore(store),
)

Queries are cached only when .Cache(ttl, tags...) is present.

Middleware

type AuditMiddleware struct {
quark.BaseMiddleware
}

func (m *AuditMiddleware) WrapQuery(next quark.QueryFunc) quark.QueryFunc {
return func(ctx context.Context, exec quark.Executor, sqlStr string, args []any) (*sql.Rows, error) {
rows, err := next(ctx, exec, sqlStr, args)
auditQuery(ctx, sqlStr, err)
return rows, err
}
}

client, err := quark.New("postgres", dsn,
quark.WithMiddleware(&AuditMiddleware{}),
)

Middleware can wrap:

MethodPath
WrapQueryQueryContext, used by list and streaming reads.
WrapQueryRowQueryRowContext, used by counts, inserts with returning, aggregates.
WrapExecExecContext, used by writes and DDL helpers.

Multiple middleware are executed in registration order.

Query Observers

type MetricsObserver struct{}

func (m *MetricsObserver) ObserveQuery(event quark.QueryEvent) {
metrics.Record(event.Table, event.Operation, event.Duration, event.Rows, event.Error)
}

client, err := quark.New("postgres", dsn,
quark.WithQueryObserver(&MetricsObserver{}),
)

QueryEvent fields:

FieldTypeDescription
SQLstringSQL sent to the driver.
Args[]anyBound arguments.
Durationtime.DurationExecution duration measured by Quark.
Rowsint64Rows returned or affected when known.
ErrorerrorNil on success.
TablestringModel table when known.
OperationstringSELECT, EXEC, QUERY_ROW, RAW_QUERY, RAW_EXEC, etc.