# Query limits

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 `connect` | TypeScript change |
| --- | --- |
| `limits.find` | `find()`, `descendants()`, and `ancestors()` may omit the argument; `limit` on the options object is optional |
| `limits.page` | `page()` may omit the argument; `limit` is optional |
| `limits.include` | A to-many `include` may omit `limit` |
| `limits.groupBy` | A grouped `aggregate` may omit `limit` |

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

```ts
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`.

| Key | Applies to |
| --- | --- |
| `find` | `find`, `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. |
| `include` | A to-many `include` (`many` and `manyThrough`, at any depth) with no `limit`. A to-one include has no limit. |
| `page` | The page size of `page()` with no `limit`. |
| `groupBy` | `aggregate` with `groupBy` and no `limit`. |
| `max` | A ceiling for the `limit` you write, on `find`, `descendants`, `ancestors`, `page()`, `aggregate`, and `include`. |

```ts
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.

```ts
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.

```ts
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](/docs/reference/warnings).

Index: [/llms.txt](/llms.txt)
