Operate

Query limits

Opt-in default limits for find, tree walks, to-many include, page, and groupBy, with a hard ceiling.

By default a read with no limit is refused (OKM1101 for find, descendants, ancestors, page, and a grouped aggregate; OKM1105 for a to-many include). limits is an opt-in default for those calls. Leave it out and nothing changes: TypeScript still requires the argument and a limit on each of those calls.

When you set a key, TypeScript makes that call's argument and limit optional for that key only:

Key set in connectTypeScript change
limits.findfind(), descendants(), and ancestors() may omit the argument; limit on the options object is optional
limits.pagepage() may omit the argument; limit is optional
limits.includeA to-many include may omit limit
limits.groupByA grouped aggregate may omit limit

Keys you leave out keep today's rule. max does not change which arguments TypeScript requires.

import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";
import { warnings } from "okmodel/warnings";

const notesTable = table("notes", { id: t.identity(), title: t.text() });
const app = schema({ tables: [notesTable] });

export const db = connect(process.env.DATABASE_URL ?? "", {
  schema: app,
  limits: { find: 50, include: 20, page: 25, groupBy: 100, max: 1000 },
  warnings: warnings(),
});

Every key is optional and independent. include and groupBy never take the value of find.

KeyApplies to
findfind, descendants, and ancestors with no limit and no .all(reason). That includes search, matches, presets, and relation filters. depth on a tree walk is steps, not this row bound.
includeA to-many include (many and manyThrough, at any depth) with no limit. A to-one include has no limit.
pageThe page size of page() with no limit.
groupByaggregate with groupBy and no limit.
maxA ceiling for the limit you write, on find, descendants, ancestors, page(), aggregate, and include.
const notes = await db.notes.find({ orderBy: { id: "asc" } }); // limit 50
const few = await db.notes.find({ orderBy: { id: "asc" }, limit: 5 }); // limit 5
const every = await db.notes.find({ orderBy: { id: "asc" } }).all("nightly export"); // no limit

A call that states a limit or .all(reason) never takes a default.

Set defaults once

okm init writes this object on connect. The library never turns a default on by itself. Leave limits out and find() still needs a limit (OKM1101). With limits.find set, a bare find() uses that default.

import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";

const notesTable = table("notes", { id: t.identity(), title: t.text() });
const app = schema({ tables: [notesTable] });

export const db = connect(process.env.DATABASE_URL ?? "", {
  schema: app,
  limits: { find: 100, include: 20, page: 50, groupBy: 500, max: 1000 },
});

const rows = await db.notes.find();

A limit on the call, or .all(reason), still wins.

max

max is a hard ceiling. A limit above it is OKM1101, and the fix is .all(reason), which reads past max on purpose.

await db.notes.find({ orderBy: { id: "asc" }, limit: 5000 }); // OKM1101
await db.notes.find({ orderBy: { id: "asc" }, limit: 5000 }).all("backfill"); // allowed

What limits do not touch

.stream(), one, count, exists, an aggregate without groupBy, the limit of update and delete, bulk write predicates, and raw().

Check at connect

limits is checked once, when the client connects. Each key is a positive integer, the keys are find, include, page, groupBy, and max, and no key is above max. A bad value is OKM1120 and rejects connected (and the first query).

See the limit

.inspect() lists the bounded rule with the source: limits.find, limits.page, limits.groupBy, or limits.include (provenance connect). A limit you wrote shows as limit (provenance caller). The onQuery event carries limit: { value, source } for the root read.

OKM2011

A default can hide rows. With warnings on, a read that took a default and returned exactly that many rows is OKM2011 in development: the call site, the key (limits.find = 50), and the fix. descendants and ancestors take limits.find and do not emit OKM2011. The fix is a limit that fits the call, page(), or .all(reason).

The warning never fires for a limit you wrote, for .all(reason), or for page(). In production every warning is off until levels turns it on. See Warnings.