Skip to main content
Version: 1.0.0

Installation

Quark is a regular Go module built on top of database/sql. You install the ORM once, then add the driver package for the database engine your application uses.

Requirements​

RequirementNotes
Go 1.21+Quark uses generics and the modern module-aware Go toolchain.
A database/sql driverThe driver owns the wire protocol and DSN format. Imported with the blank-import idiom.
Driver name passed to quark.NewQuark auto-detects the dialect from the driver name; override with WithDialect when registering a custom dialect.
go get github.com/jcsvwinston/quark
import "github.com/jcsvwinston/quark"

Quark is currently a v0.x library. The query builder, CRUD methods, transactions, batch operations, schema helpers, cache abstraction, and tenant router are present in the public package. A standalone cmd/quark binary is not part of the current module tree, so migration workflows should use the Go APIs documented in Migrations and Sync.

Driver Packages​

Install exactly the driver you need. Quark does not wrap or replace driver DSNs.

EngineSuggested driverDialect option
SQLitemodernc.org/sqlitequark.SQLite()
PostgreSQLgithub.com/lib/pqquark.PostgreSQL()
MySQLgithub.com/go-sql-driver/mysqlquark.MySQL()
MariaDBgithub.com/go-sql-driver/mysqlquark.MariaDB()
SQL Servergithub.com/microsoft/go-mssqldbquark.MSSQL()
Oraclegithub.com/sijms/go-ora/v2quark.Oracle()
go get modernc.org/sqlite
go get github.com/lib/pq
go get github.com/go-sql-driver/mysql
go get github.com/microsoft/go-mssqldb
go get github.com/sijms/go-ora/v2

SQLite is the simplest local starting point because it needs no external service. For MySQL and MariaDB, include parseTime=true in the DSN when you scan time.Time fields.

Optional Packages​

PackagePurpose
github.com/jcsvwinston/quark/cache/memoryIn-process CacheStore with tag invalidation.
github.com/jcsvwinston/quark/cache/redisRedis-backed CacheStore.
github.com/jcsvwinston/quark/otelOpenTelemetry middleware.
github.com/jcsvwinston/quark/migrateVersioned migration registry and migrator.

The core package has no framework dependency. You can use it from HTTP handlers, workers, CLIs, tests, or any service layer that can pass a context.Context.

Minimal Client​

package main

import (
"context"
"log"

"github.com/jcsvwinston/quark"
_ "modernc.org/sqlite"
)

type User struct {
ID int64 `db:"id" pk:"true"`
Email string `db:"email" quark:"unique,not_null"`
Name string `db:"name" quark:"not_null"`
}

func main() {
client, err := quark.New("sqlite", "file:quark.db?cache=shared")
if err != nil {
log.Fatal(err)
}
defer client.Close()

ctx := context.Background()
if err := client.Migrate(ctx, &User{}); err != nil {
log.Fatal(err)
}

user := User{Email: "alice@example.com", Name: "Alice"}
if err := quark.For[User](ctx, client).Create(&user); err != nil {
log.Fatal(err)
}

log.Printf("created user id=%d", user.ID)
}

quark.New pings the database during construction. Treat a returned error as a startup failure: wrong DSN, unreachable database, credentials rejected, or a driver/dialect mismatch.

Switching Engines​

The model and query code do not change when you change the database. The driver import and the driver name passed to quark.New do.

import (
"github.com/jcsvwinston/quark"
_ "github.com/lib/pq"
)

client, err := quark.New("postgres", "postgres://user:pass@localhost/app?sslmode=disable")
if err != nil {
return err
}

The dialect is auto-detected from the driver name. It controls placeholder syntax, identifier quoting, RETURNING behavior, upsert fragments, JSON expressions, pagination, and DDL.

Pool Configuration​

Quark owns the *sql.DB it creates. Configure the pool through the WithMaxOpenConns, WithMaxIdleConns, and WithConnMaxLifetime options:

client, err := quark.New("postgres", dsn,
quark.WithMaxOpenConns(25),
quark.WithMaxIdleConns(25),
quark.WithConnMaxLifetime(30*time.Minute),
)

For database-per-tenant routing, each tenant can own its own *sql.DB pool. See Multi-Tenant before setting high pool limits.

Smoke Test​

Use this to verify that the driver, dialect, migration helper, insert path, and primary-key writeback all work:

func Smoke(ctx context.Context, client *quark.Client) error {
type HealthCheck struct {
ID int64 `db:"id" pk:"true"`
Name string `db:"name" quark:"not_null"`
}

if err := client.Migrate(ctx, &HealthCheck{}); err != nil {
return err
}

row := HealthCheck{Name: "ok"}
if err := quark.For[HealthCheck](ctx, client).Create(&row); err != nil {
return err
}

_, err := quark.For[HealthCheck](ctx, client).Find(row.ID)
return err
}

Integration Test DSNs​

The upstream test suite uses environment variables for external engines. SQLite tests can run offline.

export QUARK_TEST_POSTGRES_DSN="postgres://user:pass@localhost:5432/testdb?sslmode=disable"
export QUARK_TEST_MYSQL_DSN="user:pass@tcp(localhost:3306)/testdb?parseTime=true"
export QUARK_TEST_MSSQL_DSN="sqlserver://sa:Pass@localhost:1433?database=testdb"
export QUARK_TEST_ORACLE_DSN="oracle://user:pass@localhost:1521/XE"

When a test or local tool uses raw DDL through client.Exec, configure the client with AllowRawQueries: true. The high-level Migrate, Sync, CreateIndex, and AddForeignKey helpers do not require raw-query opt-in.