Skip to main content
Version: 0.7.0

Querying API Reference

Reference for read execution methods on *quark.Query[T].

First

First() (T, error)

Returns the first matching row or quark.ErrNotFound.

user, err := quark.For[User](ctx, client).
Where("active", "=", true).
OrderBy("created_at", "DESC").
First()

Behavior:

CaseReturn
Row exists(entity, nil)
No row(zero, quark.ErrNotFound)
Query or scan error(zero, error)

First sets Limit(1) internally.

Find

Find(id any) (T, error)

Finds one row by simple primary key.

user, err := quark.For[User](ctx, client).Find(42)

Find is for single-column primary keys. For composite primary keys, use Where(...).First():

item, err := quark.For[OrderItem](ctx, client).
Where("order_id", "=", orderID).
Where("product_id", "=", productID).
First()

Soft-delete filters and tenant filters still apply.

List

List() ([]T, error)

Executes the select and scans matching rows into []T.

users, err := quark.For[User](ctx, client).
Where("active", "=", true).
OrderBy("name", "ASC").
Limit(100).
List()

If no explicit limit has been set, List applies a safe default limit of 100 and logs a warning. Use Iter or Cursor for intentionally unbounded reads.

List respects:

FeatureBehavior
Soft deletesAdds deleted_at IS NULL unless Unscoped() is set.
Tenant routerApplies schema or row-level isolation.
CacheReads and writes cache entries when .Cache(...) is enabled.
PreloadLoads requested relations after parent rows are scanned.
MiddlewareExecutes through registered query middleware.
ObserversEmits a QueryEvent after execution.

Count

Count() (int64, error)

Counts matching rows.

count, err := quark.For[User](ctx, client).
Where("active", "=", true).
Count()

Count includes Where, joins, soft-delete filters, and tenant filters. It does not apply Limit or Offset.

Aggregates

Sum(column string) (float64, error)

total, err := quark.For[Order](ctx, client).
Where("status", "=", "paid").
Sum("amount")

Avg(column string) (float64, error)

avg, err := quark.For[Order](ctx, client).Avg("amount")

Min(column string) (float64, error)

min, err := quark.For[Product](ctx, client).
Where("stock", ">", 0).
Min("price")

Max(column string) (float64, error)

max, err := quark.For[Product](ctx, client).
Where("stock", ">", 0).
Max("price")

Aggregate column names are validated as identifiers. The helpers return 0 when the database aggregate result is NULL.

Paginate

Paginate(pageSize, page int) (*Page[T], error)

Runs a count query and a limited select.

page, err := quark.For[User](ctx, client).
Where("active", "=", true).
OrderBy("id", "ASC").
Paginate(20, 0)

fmt.Println(page.Items)
fmt.Println(page.Total)
fmt.Println(page.TotalPages)

page is zero-indexed. Negative pages are treated as 0. A non-positive pageSize defaults to 100.

type Page[T any] struct {
Items []T
Total int64
Page int
PageSize int
TotalPages int64
}

Cursor

Cursor() (*Cursor[T], error)

Returns a type-safe cursor over sql.Rows.

cursor, err := quark.For[LogEntry](ctx, client).
Where("level", "=", "error").
OrderBy("id", "ASC").
Cursor()
if err != nil {
return err
}
defer cursor.Close()

for cursor.Next() {
var entry LogEntry
if err := cursor.Scan(&entry); err != nil {
return err
}
process(entry)
}

return cursor.Err()

Cursor methods:

MethodDescription
Next() boolAdvances to the next row.
Scan(dest *T) errorScans the current row into dest.
Close() errorCloses rows, cancels the query context, and emits an observer event.
Err() errorReturns the underlying row iteration error.

Always call Close.

Iter

Iter(fn func(T) error) error

Streams rows through a callback.

err := quark.For[Event](ctx, client).
Where("processed", "=", false).
OrderBy("id", "ASC").
Iter(func(event Event) error {
if err := processEvent(event); err != nil {
return err
}
return nil
})

Iteration stops on the first callback error, scan error, query error, or rows error.

Scanning Rules

Rows are scanned by matching result columns to model fields:

Match sourceNotes
db tagFast path through cached metadata.
snake-cased field nameFallback for columns without a metadata hit.
exact field nameFallback for matching driver column names.

Unknown result columns are discarded. For custom projections, define a dedicated read model with matching db tags or use raw SQL and manual scanning.

time.Time and *time.Time fields include scanner wrappers for drivers that return datetime values as strings or byte slices.