Query Builder API
This page documents every method on Cascade’s QueryBuilderContract. It’s a reference, not a tutorial — each method shows its signature, a one-sentence description, a minimal example, and a link to the guide that teaches the concept.
Use the in-page anchors (or Ctrl-F) when you know the name and just need the signature. For the why and when, follow the See also links into the essentials and guide pages.
:::info — How method blocks are organised
Each method shows:
- Signature — the TypeScript signature(s) you can call
- What it does — one sentence, mechanical
- Example — one minimal snippet
- See also — the guide that teaches the concept
Methods are grouped by purpose (where, select, joins, ordering, …) and alphabetised within each group.
:::
Scopes
Section titled “Scopes”scope(name, ...args)
Section titled “scope(name, ...args)”scope(scopeName: string, ...args: any[]): thisWhat it does: apply a local scope defined on the model.
await User.query().scope("active").get();See also: Querying essentials
withoutGlobalScope(...names)
Section titled “withoutGlobalScope(...names)”withoutGlobalScope(...scopeNames: string[]): thisWhat it does: disable one or more global scopes for this query.
await User.query().withoutGlobalScope("tenant").get();withoutGlobalScopes()
Section titled “withoutGlobalScopes()”withoutGlobalScopes(): thisWhat it does: disable all global scopes for this query.
await User.query().withoutGlobalScopes().get();Where clauses
Section titled “Where clauses”where(...)
Section titled “where(...)”where(field: string, value: unknown): thiswhere(field: string, operator: WhereOperator, value: unknown): thiswhere(conditions: WhereObject): thiswhere(callback: WhereCallback<T>): thisWhat it does: add a where clause. Four forms — equality, operator, object, callback.
await User.where("age", ">", 18).get();await User.where({ status: "active", role: "admin" }).get();See also: Querying essentials
orWhere(...)
Section titled “orWhere(...)”orWhere(field: string, value: unknown): thisorWhere(field: string, operator: WhereOperator, value: unknown): thisorWhere(conditions: WhereObject): thisorWhere(callback: WhereCallback<T>): thisWhat it does: same shapes as where, joined with OR.
await User.where("role", "admin").orWhere("role", "moderator").get();whereIn(field, values) / whereNotIn(field, values)
Section titled “whereIn(field, values) / whereNotIn(field, values)”whereIn(field: string, values: unknown[]): thiswhereNotIn(field: string, values: unknown[]): thisWhat it does: match where field is (or isn’t) any value in the list.
await User.whereIn("status", ["active", "pending"]).get();whereNull(field) / whereNotNull(field)
Section titled “whereNull(field) / whereNotNull(field)”whereNull(field: string): thiswhereNotNull(field: string): thisWhat it does: constrain the field to be NULL or NOT NULL.
await User.whereNotNull("emailVerifiedAt").get();whereBetween(field, range) / whereNotBetween(field, range)
Section titled “whereBetween(field, range) / whereNotBetween(field, range)”whereBetween(field: string, range: [unknown, unknown]): thiswhereNotBetween(field: string, range: [unknown, unknown]): thisWhat it does: constrain the field to fall inside (or outside) the inclusive range.
await User.whereBetween("age", [18, 65]).get();whereLike / whereNotLike / whereStartsWith / whereNotStartsWith / whereEndsWith / whereNotEndsWith
Section titled “whereLike / whereNotLike / whereStartsWith / whereNotStartsWith / whereEndsWith / whereNotEndsWith”whereLike(field: string, pattern: RegExp | string): thiswhereNotLike(field: string, pattern: string): thiswhereStartsWith(field: string, value: string | number): thiswhereNotStartsWith(field: string, value: string | number): thiswhereEndsWith(field: string, value: string | number): thiswhereNotEndsWith(field: string, value: string | number): thisWhat it does: pattern-match (or anti-match) on a string field. Case-insensitive.
await User.whereLike("name", "%john%").get();await User.whereStartsWith("email", "admin@").get();whereDate / whereDateEquals / whereDateBefore / whereDateAfter / whereDateBetween / whereDateNotBetween
Section titled “whereDate / whereDateEquals / whereDateBefore / whereDateAfter / whereDateBetween / whereDateNotBetween”whereDate(field: string, value: Date | string): thiswhereDateEquals(field: string, value: Date | string): thiswhereDateBefore(field: string, value: Date | string): thiswhereDateAfter(field: string, value: Date | string): thiswhereDateBetween(field: string, range: [Date, Date]): thiswhereDateNotBetween(field: string, range: [Date, Date]): thisWhat it does: filter on a date field — exact day, before/after, or a range. Time portion is ignored for the day-equality variants.
await Order.whereDateBetween("createdAt", [start, end]).get();whereTime / whereDay / whereMonth / whereYear
Section titled “whereTime / whereDay / whereMonth / whereYear”whereTime(field: string, value: string): thiswhereDay(field: string, value: number): thiswhereMonth(field: string, value: number): thiswhereYear(field: string, value: number): thisWhat it does: filter on an extracted part of a date field.
await Event.whereMonth("startsAt", 12).get();whereId(value) / whereIds(values) / whereUuid(value) / whereUlid(value)
Section titled “whereId(value) / whereIds(values) / whereUuid(value) / whereUlid(value)”whereId(value: string | number): thiswhereIds(values: Array<string | number>): thiswhereUuid(value: string): thiswhereUlid(value: string): thisWhat it does: shortcuts for filtering by primary key (single, multiple, or by id-type variant).
await User.whereIds([1, 2, 3]).get();whereFullText / orWhereFullText / whereSearch / textSearch
Section titled “whereFullText / orWhereFullText / whereSearch / textSearch”whereFullText(fields: string | string[], query: string): thisorWhereFullText(fields: string | string[], query: string): thiswhereSearch(field: string, query: string): thistextSearch(query: string, filters?: WhereObject): thisWhat it does: full-text search variants. whereFullText constrains specific fields; textSearch does a driver-native full-text query with optional filters.
await Post.whereFullText(["title", "body"], "cascade orm").get();whereNot(callback) / orWhereNot(callback)
Section titled “whereNot(callback) / orWhereNot(callback)”whereNot(callback: WhereCallback<T>): thisorWhereNot(callback: WhereCallback<T>): thisWhat it does: negate a nested predicate block.
await User.whereNot(q => q.where("status", "banned")).get();whereExists(...) / whereNotExists(...)
Section titled “whereExists(...) / whereNotExists(...)”whereExists(callback: WhereCallback<T>): thiswhereExists(field: string): thiswhereNotExists(callback: WhereCallback<T>): thiswhereNotExists(field: string): thisWhat it does: existence check — either a nested condition exists, or (MongoDB-style) the field itself is present in the document.
await User.whereExists("optionalField").get();await User.whereExists(q => q.where("status", "active")).get();whereColumn / orWhereColumn / whereColumns / whereBetweenColumns
Section titled “whereColumn / orWhereColumn / whereColumns / whereBetweenColumns”whereColumn(first: string, operator: WhereOperator, second: string): thisorWhereColumn(first: string, operator: WhereOperator, second: string): thiswhereColumns(comparisons: Array<[string, WhereOperator, string]>): thiswhereBetweenColumns(field: string, lowerColumn: string, upperColumn: string): thisWhat it does: compare one column to another, instead of to a literal value.
await Product.whereColumn("stock", ">", "reserved").get();whereSize(field, size) / whereSize(field, operator, size)
Section titled “whereSize(field, size) / whereSize(field, operator, size)”whereSize(field: string, size: number): thiswhereSize(field: string, operator: ">" | ">=" | "=" | "<" | "<=", size: number): thisWhat it does: filter on the size of an array/collection field.
await Post.whereSize("tags", ">", 0).get();whereRaw(expression, bindings?) / orWhereRaw(expression, bindings?)
Section titled “whereRaw(expression, bindings?) / orWhereRaw(expression, bindings?)”whereRaw(expression: RawExpression, bindings?: unknown[]): thisorWhereRaw(expression: RawExpression, bindings?: unknown[]): thisWhat it does: drop into the driver’s native query language for a where clause Cascade can’t express.
await User.whereRaw("this.age > ?", [30]).get();JSON & array helpers
Section titled “JSON & array helpers”whereJsonContains(path, value) / whereJsonDoesntContain(path, value)
Section titled “whereJsonContains(path, value) / whereJsonDoesntContain(path, value)”whereJsonContains(path: string, value: unknown): thiswhereJsonDoesntContain(path: string, value: unknown): thisWhat it does: test whether a JSON path contains a given value.
await Post.whereJsonContains("tags", "javascript").get();See also: JSON fields guide
whereJsonContainsKey(path)
Section titled “whereJsonContainsKey(path)”whereJsonContainsKey(path: string): thisWhat it does: test whether a JSON path exists (key is present).
await User.whereJsonContainsKey("profile.avatar").get();whereJsonLength(path, operator, value)
Section titled “whereJsonLength(path, operator, value)”whereJsonLength(path: string, operator: WhereOperator, value: number): thisWhat it does: filter on the length of an array or object at a JSON path.
await Post.whereJsonLength("tags", ">", 3).get();whereJsonIsArray(path) / whereJsonIsObject(path)
Section titled “whereJsonIsArray(path) / whereJsonIsObject(path)”whereJsonIsArray(path: string): thiswhereJsonIsObject(path: string): thisWhat it does: assert the value at a JSON path is of the expected shape.
await User.whereJsonIsArray("profile.roles").get();whereArrayContains / whereArrayNotContains / whereArrayHasOrEmpty / whereArrayNotHaveOrEmpty
Section titled “whereArrayContains / whereArrayNotContains / whereArrayHasOrEmpty / whereArrayNotHaveOrEmpty”whereArrayContains(field: string, value: unknown, key?: string): thiswhereArrayNotContains(field: string, value: unknown, key?: string): thiswhereArrayHasOrEmpty(field: string, value: unknown, key?: string): thiswhereArrayNotHaveOrEmpty(field: string, value: unknown, key?: string): thisWhat it does: array-flavoured containment checks. The optional key argument matches on a nested property when the array holds objects.
await Post.whereArrayContains("tags", "javascript").get();await Order.whereArrayContains("items", "laptop", "name").get();whereArrayLength(field, operator, value)
Section titled “whereArrayLength(field, operator, value)”whereArrayLength(field: string, operator: WhereOperator, value: number): thisWhat it does: filter on the length of an array column.
await User.whereArrayLength("roles", ">=", 2).get();Select / projection
Section titled “Select / projection”select(...)
Section titled “select(...)”select(fields: string[]): thisselect(fields: Record<string, 0 | 1 | boolean>): thisselect(...fields: Array<string | string[]>): thisWhat it does: choose which columns/fields the query returns. Four call shapes — array, projection object, rest-args, or mixed.
await User.query().select(["name", "email"]).get();See also: Querying essentials
selectAs(field, alias)
Section titled “selectAs(field, alias)”selectAs(field: string, alias: string): thisWhat it does: select a single field under a new alias.
query.selectAs("name", "fullName");addSelect(fields)
Section titled “addSelect(fields)”addSelect(fields: string[]): thisWhat it does: append fields to the existing projection without clearing it.
query.select(["name", "email"]).addSelect(["age"]);selectRaw(expression, bindings?) / selectRawMany(definitions)
Section titled “selectRaw(expression, bindings?) / selectRawMany(definitions)”selectRaw(expression: RawExpression, bindings?: unknown[]): thisselectRawMany(definitions: Array<{ alias: string; expression: RawExpression; bindings?: unknown[] }>): thisWhat it does: add a raw projection expression (selectRaw), or several at once (selectRawMany).
query.selectRaw({ total: { $sum: "$items.price" } });selectSub(expression, alias) / addSelectSub(expression, alias)
Section titled “selectSub(expression, alias) / addSelectSub(expression, alias)”selectSub(expression: RawExpression, alias: string): thisaddSelectSub(expression: RawExpression, alias: string): thisWhat it does: inject a sub-select expression under an alias. addSelectSub does the same without clearing existing selects.
query.selectSub({ $sum: "$items.price" }, "itemsTotal");selectAggregate(field, aggregate, alias)
Section titled “selectAggregate(field, aggregate, alias)”selectAggregate( field: string, aggregate: "sum" | "avg" | "min" | "max" | "count" | "first" | "last", alias: string,): thisWhat it does: project a single aggregate as a named field on the result.
query.selectAggregate("items.price", "sum", "itemsTotal");See also: Aggregates guide
selectExists(field, alias) / selectCount(field, alias)
Section titled “selectExists(field, alias) / selectCount(field, alias)”selectExists(field: string, alias: string): thisselectCount(field: string, alias: string): thisWhat it does: project a boolean (selectExists) or a count (selectCount) of items at a path.
query.selectExists("deletedAt", "isDeleted");query.selectCount("permissions", "permissionsCount");selectCase(cases, otherwise, alias) / selectWhen(condition, then, else, alias)
Section titled “selectCase(cases, otherwise, alias) / selectWhen(condition, then, else, alias)”selectCase( cases: Array<{ when: RawExpression; then: RawExpression | unknown }>, otherwise: RawExpression | unknown, alias: string,): thisselectWhen( condition: RawExpression, thenValue: RawExpression | unknown, elseValue: RawExpression | unknown, alias: string,): thisWhat it does: project a CASE/switch expression. selectWhen is the single-condition shortcut.
query.selectWhen({ $gt: ["$age", 18] }, "Adult", "Minor", "ageLabel");selectJson(path, alias?) / selectJsonRaw(path, expression, alias) / deselectJson(path)
Section titled “selectJson(path, alias?) / selectJsonRaw(path, expression, alias) / deselectJson(path)”selectJson(path: string, alias?: string): thisselectJsonRaw(path: string, expression: RawExpression, alias: string): thisdeselectJson(path: string): thisWhat it does: project a nested JSON path (selectJson), apply a raw expression to it (selectJsonRaw), or exclude it (deselectJson).
query.selectJson("profile.address.city", "city");See also: JSON fields guide
selectConcat(fields, alias) / selectCoalesce(fields, alias)
Section titled “selectConcat(fields, alias) / selectCoalesce(fields, alias)”selectConcat(fields: Array<string | RawExpression>, alias: string): thisselectCoalesce(fields: Array<string | RawExpression>, alias: string): thisWhat it does: project a concatenation, or the first non-null among several values.
query.selectConcat(["$firstName", " ", "$lastName"], "fullName");query.selectCoalesce(["$nickname", "$name"], "displayName");selectWindow(spec)
Section titled “selectWindow(spec)”selectWindow(spec: RawExpression): thisWhat it does: attach a window function expression to the projection (rank, partition aggregates, running totals, …).
query.selectWindow({ partitionBy: "$category", sortBy: { createdAt: 1 }, output: { rank: { $denseRank: {} } },});selectDriverProjection(callback)
Section titled “selectDriverProjection(callback)”selectDriverProjection(callback: (projection: Record<string, unknown>) => void): thisWhat it does: mutate the driver’s underlying projection object directly. Last-resort escape hatch.
query.selectDriverProjection(p => { p.score = { $meta: "textScore" }; });deselect(fields) / clearSelect() / selectAll() / selectDefault()
Section titled “deselect(fields) / clearSelect() / selectAll() / selectDefault()”deselect(fields: string[]): thisclearSelect(): thisselectAll(): thisselectDefault(): thisWhat it does: exclude specific fields (deselect), or reset the projection. clearSelect, selectAll, and selectDefault all restore the “all columns” default.
query.deselect(["password", "token"]);distinctValues(fields?)
Section titled “distinctValues(fields?)”distinctValues(fields?: string | string[]): thisWhat it does: mark the query as returning distinct rows for the given fields.
query.distinctValues(["category", "status"]);join(table, localField, foreignField) / join(options)
Section titled “join(table, localField, foreignField) / join(options)”join(table: string, localField: string, foreignField: string): thisjoin(options: JoinOptions): thisWhat it does: INNER JOIN on SQL; $lookup on MongoDB (which is inherently left-flavoured — see the joins guide).
query.join("profiles", "id", "user_id");See also: Joins guide
innerJoin(table, localField, foreignField) / innerJoin(options)
Section titled “innerJoin(table, localField, foreignField) / innerJoin(options)”innerJoin(table: string, localField: string, foreignField: string): thisinnerJoin(options: JoinOptions): thisWhat it does: explicit INNER JOIN with cross-driver semantics (MongoDB adds a $match to drop empty joins).
leftJoin(table, localField, foreignField) / leftJoin(options)
Section titled “leftJoin(table, localField, foreignField) / leftJoin(options)”leftJoin(table: string, localField: string, foreignField: string): thisleftJoin(options: JoinOptions): thisWhat it does: LEFT JOIN — keep every row on the left, fill nulls on the right when there’s no match.
rightJoin(table, localField, foreignField) / rightJoin(options)
Section titled “rightJoin(table, localField, foreignField) / rightJoin(options)”rightJoin(table: string, localField: string, foreignField: string): thisrightJoin(options: JoinOptions): thisWhat it does: RIGHT JOIN on SQL. Silently emulated as LEFT JOIN on MongoDB — see the joins guide before using.
fullJoin(table, localField, foreignField) / fullJoin(options)
Section titled “fullJoin(table, localField, foreignField) / fullJoin(options)”fullJoin(table: string, localField: string, foreignField: string): thisfullJoin(options: JoinOptions): thisWhat it does: FULL OUTER JOIN on SQL. Silently emulated as LEFT JOIN on MongoDB — see the joins guide.
crossJoin(table)
Section titled “crossJoin(table)”crossJoin(table: string): thisWhat it does: Cartesian product. Every row on the left × every row on the right.
query.crossJoin("colors");joinRaw(expression, bindings?)
Section titled “joinRaw(expression, bindings?)”joinRaw(expression: RawExpression, bindings?: unknown[]): thisWhat it does: raw join expression in driver-native syntax. Use for $graphLookup, lateral joins, anything the structured methods can’t express.
query.joinRaw("LEFT JOIN LATERAL (...) e ON true");Relations & eager loading
Section titled “Relations & eager loading”with(...)
Section titled “with(...)”with(relation: string): thiswith(...relations: string[]): thiswith(relation: string, constraint: (query: QueryBuilderContract) => void): thiswith(relations: Record<string, boolean | ((query: QueryBuilderContract) => void)>): thisWhat it does: eager-load related models in separate, optimised queries (avoids N+1).
await User.query().with("posts", "organization").find(1);await User.query() .with({ posts: q => q.where("isPublished", true) }) .find(1);See also: Relationships essentials
joinWith(...relations)
Section titled “joinWith(...relations)”joinWith(...relations: string[]): thisWhat it does: load relations via JOIN (single query) instead of separate queries. Best for belongsTo / hasOne.
const post = await Post.joinWith("author").first();withCount(...)
Section titled “withCount(...)”withCount(relation: string): thiswithCount(...relations: string[]): thiswithCount(relations: string[]): thiswithCount(relations: Record<string, true | string | ((query: QueryBuilderContract) => void)>): thisWhat it does: project the count of a related model as a virtual field. Default alias ${relation}Count; override with as <alias> or the object value form.
await User.query().withCount("posts").get();await Post.query() .withCount({ comments: true, "comments as approvedCount": q => q.where("approved", true), }) .get();has(relation) / has(relation, operator, count)
Section titled “has(relation) / has(relation, operator, count)”has(relation: string): thishas(relation: string, operator: string, count: number): thisWhat it does: filter results to those that have related models (with optional count constraint).
await User.query().has("posts").get();await User.query().has("posts", ">=", 5).get();whereHas(relation, callback) / doesntHave(relation) / whereDoesntHave(relation, callback)
Section titled “whereHas(relation, callback) / doesntHave(relation) / whereDoesntHave(relation, callback)”whereHas(relation: string, callback: (query: QueryBuilderContract) => void): thisdoesntHave(relation: string): thiswhereDoesntHave(relation: string, callback: (query: QueryBuilderContract) => void): thisWhat it does: existence/non-existence checks on relations, with optional callback to constrain the related query.
await User.query() .whereHas("posts", q => q.where("isPublished", true)) .get();Ordering
Section titled “Ordering”orderBy(field, direction?) / orderBy(fields)
Section titled “orderBy(field, direction?) / orderBy(fields)”orderBy(field: string, direction?: OrderDirection): thisorderBy(fields: Record<string, OrderDirection>): thisWhat it does: order results by one or more fields.
await User.query().orderBy("createdAt", "desc").get();await User.query().orderBy({ status: "asc", createdAt: "desc" }).get();See also: Querying essentials
orderByDesc(field)
Section titled “orderByDesc(field)”orderByDesc(field: string): thisWhat it does: descending shortcut for orderBy(field, "desc").
orderByRaw(expression, bindings?)
Section titled “orderByRaw(expression, bindings?)”orderByRaw(expression: RawExpression, bindings?: unknown[]): thisWhat it does: order by a raw driver expression.
query.orderByRaw("RANDOM()");orderByRandom(limit)
Section titled “orderByRandom(limit)”orderByRandom(limit: number): thisWhat it does: randomly order results; takes a limit to avoid scanning the whole table.
latest(column?) / oldest(column?)
Section titled “latest(column?) / oldest(column?)”latest(column?: string): Promise<T[]>oldest(column?: string): thisWhat it does: order by a timestamp column descending (latest) or ascending (oldest). Default column is createdAt. Note: latest is a terminator and returns the records directly.
const newest = await Post.query().latest();Limiting & pagination
Section titled “Limiting & pagination”limit(value) / take(value)
Section titled “limit(value) / take(value)”limit(value: number): thistake(value: number): thisWhat it does: cap the number of returned rows. take is an alias.
skip(value) / offset(value)
Section titled “skip(value) / offset(value)”skip(value: number): thisoffset(value: number): thisWhat it does: skip the given number of rows. Aliases for each other.
cursor(after?, before?)
Section titled “cursor(after?, before?)”cursor(after?: unknown, before?: unknown): thisWhat it does: apply cursor-pagination hints. Pair with cursorPaginate for the full pattern.
paginate(options)
Section titled “paginate(options)”paginate(options: PaginationOptions): Promise<PaginationResult<T>>What it does: terminator — page/limit pagination with total + page count.
const result = await User.query().paginate({ page: 1, limit: 10 });// { data, pagination: { total, page, limit, pages } }See also: Querying essentials
cursorPaginate(options)
Section titled “cursorPaginate(options)”cursorPaginate(options: CursorPaginationOptions): Promise<CursorPaginationResult<T>>What it does: terminator — cursor-based pagination. Better than offset for large result sets and infinite-scroll UIs.
const result = await User.query().cursorPaginate({ limit: 10, cursor: lastId });// { data, pagination: { hasMore, nextCursor, ... } }chunk(size, callback)
Section titled “chunk(size, callback)”chunk(size: number, callback: ChunkCallback<T>): Promise<void>What it does: terminator — iterate through results in chunks. Callback returns false to stop.
await User.query().chunk(100, async (users, index) => { await processUsers(users);});Grouping & aggregation
Section titled “Grouping & aggregation”groupBy(fields) / groupBy(fields, aggregates)
Section titled “groupBy(fields) / groupBy(fields, aggregates)”groupBy(fields: GroupByInput): thisgroupBy(fields: GroupByInput, aggregates: Record<string, RawExpression>): thisWhat it does: group results by one or more fields, with optional aggregate output. Works on both MongoDB and Postgres. The driver-agnostic $agg.count/sum/avg/min/max are cross-driver; $agg.distinct/floor/first/last are MongoDB-only and throw on Postgres (see the Aggregates guide).
import { $agg } from "@warlock.js/cascade";
query.groupBy("category", { total: $agg.sum("amount"), count: $agg.count(),});See also: Aggregates guide
groupByRaw(expression, bindings?)
Section titled “groupByRaw(expression, bindings?)”groupByRaw(expression: RawExpression, bindings?: unknown[]): thisWhat it does: group by a raw expression (DATE(createdAt), custom JSON path, …).
having(...) / havingRaw(expression, bindings?)
Section titled “having(...) / havingRaw(expression, bindings?)”having(field: string, value: unknown): thishaving(field: string, operator: WhereOperator, value: unknown): thishaving(condition: HavingInput): thishavingRaw(expression: RawExpression, bindings?: unknown[]): thisWhat it does: filter the grouped/aggregated results (vs .where() which filters before grouping).
query.groupBy("category", { total: $agg.sum("amount") }) .having("total", ">", 1000);Execution / terminators
Section titled “Execution / terminators”get<Output>()
Section titled “get<Output>()”get<Output = T>(): Promise<Output[]>What it does: execute the query and return all matching records.
const users = await User.query().where("isActive", true).get();first<Output>() / firstOrFail<Output>()
Section titled “first<Output>() / firstOrFail<Output>()”first<Output = T>(): Promise<Output | null>firstOrFail<Output = T>(): Promise<Output>What it does: return the first matching record (or null / throw if none).
const user = await User.where("email", "john@example.com").first();last<Output>(field?)
Section titled “last<Output>(field?)”last<Output = T>(field?: string): Promise<Output | null>What it does: return the last record by the given field (default: primary key descending).
count()
Section titled “count()”count(): Promise<number>What it does: count matching records.
const total = await User.where("isActive", true).count();sum(field) / avg(field) / min(field) / max(field)
Section titled “sum(field) / avg(field) / min(field) / max(field)”sum(field: string): Promise<number>avg(field: string): Promise<number>min(field: string): Promise<number>max(field: string): Promise<number>What it does: scalar aggregates over the matched rows. Each returns a Promise<number>.
const revenue = await Order.where("status", "completed").sum("amount");distinct<T>(field) / countDistinct(field)
Section titled “distinct<T>(field) / countDistinct(field)”distinct<T = unknown>(field: string): Promise<T[]>countDistinct(field: string): Promise<number>What it does: distinct values for a field (distinct returns the values; countDistinct returns the count of distinct values).
const categories = await Product.query().distinct<string>("category");pluck(field)
Section titled “pluck(field)”pluck(field: string): Promise<unknown[]>What it does: retrieve a flat array of a single field’s values across all matching rows.
const names = await User.query().pluck("name");value<T>(field)
Section titled “value<T>(field)”value<T = unknown>(field: string): Promise<T | null>What it does: scalar — retrieve a single field’s value from the first matching row.
const email = await User.whereId(123).value<string>("email");exists() / notExists()
Section titled “exists() / notExists()”exists(): Promise<boolean>notExists(): Promise<boolean>What it does: does any row (or no row) match? Cheaper than count() > 0.
const hasActiveUsers = await User.where("isActive", true).exists();increment(field, amount?) / decrement(field, amount?)
Section titled “increment(field, amount?) / decrement(field, amount?)”increment(field: string, amount?: number): Promise<number>decrement(field: string, amount?: number): Promise<number>What it does: atomically increment/decrement a field. Returns the new value. Default amount: 1.
await User.whereId(123).increment("loginCount");incrementMany(field, amount?) / decrementMany(field, amount?)
Section titled “incrementMany(field, amount?) / decrementMany(field, amount?)”incrementMany(field: string, amount?: number): Promise<number>decrementMany(field: string, amount?: number): Promise<number>What it does: apply the increment/decrement to every matching row. Returns the number of rows modified.
Inspection & debugging
Section titled “Inspection & debugging”parse()
Section titled “parse()”parse(): DriverQueryWhat it does: return the driver-native query without executing. SQL drivers populate { query, bindings }; MongoDB populates { pipeline }.
const { query, bindings } = User.where("age", ">", 18).parse();pretty()
Section titled “pretty()”pretty(): stringWhat it does: formatted string representation of the parsed query.
explain()
Section titled “explain()”explain(): Promise<unknown>What it does: ask the driver for the query’s execution plan.
const plan = await User.where("status", "active").explain();Lifecycle hooks
Section titled “Lifecycle hooks”onFetching(callback)
Section titled “onFetching(callback)”onFetching(callback: (query: this) => void | Promise<void>): () => voidWhat it does: register a callback to run before query execution. Returns an unsubscribe function.
onHydrating(callback)
Section titled “onHydrating(callback)”onHydrating(callback: (records: any[], context: any) => void | Promise<void>): () => voidWhat it does: register a callback to run after fetching raw rows but before hydrating them into model instances.
onFetched(callback)
Section titled “onFetched(callback)”onFetched(callback: (records: any[], context: any) => void | Promise<void>): () => voidWhat it does: register a callback to run after hydration. Receives the hydrated model instances.
hydrate(callback)
Section titled “hydrate(callback)”hydrate(callback: (data: any, index: number) => any): thisWhat it does: transform each result row during hydration.
Utility
Section titled “Utility”tap(callback)
Section titled “tap(callback)”tap(callback: (builder: this) => void): thisWhat it does: side-effect into the chain (logging, debugging) without breaking the fluent flow.
query.where("age", ">", 18).tap(q => console.log(q.parse())).get();when<V>(condition, callback, otherwise?)
Section titled “when<V>(condition, callback, otherwise?)”when<V>( condition: V | boolean | (() => boolean), callback: (builder: this, value: V) => void, otherwise?: (builder: this) => void,): thisWhat it does: conditionally apply a callback to the query. Useful for optional filters from controller input.
query.when(searchTerm, (q, term) => q.whereLike("name", term));clone()
Section titled “clone()”clone(): thisWhat it does: duplicate the query builder so you can branch off without mutating the original.
const base = User.query().where("isActive", true);const admins = base.clone().where("role", "admin");raw(builder)
Section titled “raw(builder)”raw(builder: (native: unknown) => unknown): thisWhat it does: mutate the underlying native query object directly. Last-resort escape hatch.
extend<R>(extension, ...args)
Section titled “extend<R>(extension, ...args)”extend<R>(extension: string, ...args: unknown[]): RWhat it does: invoke a driver-specific extension registered with the query builder.
Specialty
Section titled “Specialty”similarTo(column, embedding, alias?)
Section titled “similarTo(column, embedding, alias?)”similarTo(column: string, embedding: number[], alias?: string): thisWhat it does: nearest-neighbour vector similarity search. Adds a similarity score to the SELECT and an ORDER BY on the same expression so the vector index can be used. Default score alias "score".
const results = await Vector.query() .where({ organization_id: orgId, content_type: "summary" }) .similarTo("embedding", queryEmbedding) .limit(5) .get<VectorRow & { score: number }>();See also: Vector search guide