Views, ordering and pagination
Two kinds of list page differently, a view holds filters and sorts, a cursor carries the ordering that produced it, and not every field can be sorted on.
Paging
Two kinds of list exist, and they page differently.
Most lists — products, price books, quotes — take limit and after, and answer nextCursor. Pass nextCursor back as after for the next page, and stop when it is null. nextCursor is the id of the last row on the page.
limit runs from 1 to 100 and defaults to 50 on both kinds of list.
Record lists take more: limit, dir, orderBy or view, relatedTo with via, and cursor or after. Four combinations are refused with 400, and they cannot be expressed in a parameter list:
viarequiresrelatedToorderBycannot be combined withviewcursorrequires one oforderByorviewaftercannot be combined with either — page an ordered query withcursor
A cursor belongs to the ordering that produced it. The ordering — the sort, the direction, the related-record filter, the view — travels inside the cursor. Replay a cursor against a changed view and the request fails loudly with cursor_ordering_mismatch rather than answering a page that looks right and is not. Fetch from the first page again when the ordering changes.
total is counted on the first page only. A request with no after and no cursor answers a count; every later page answers total: null. The count is paid once rather than on every page, so hold the first page’s figure if you want to show one.
A view is a filter and a sort together
A view holds conditions, how they combine, and sorts: at most ten conditions and at most two sorts. match is all or any and defaults to all, so an unqualified view narrows rather than widens.
A condition names a field, an operator, and — depending on the operator — a value. There are thirty-three operators in four families, and the family decides the shape of the condition.
| Family | Count | Takes | Examples |
|---|---|---|---|
| Unary | 15 | no value | isEmpty, isTrue, isToday, isThisMonth, isPast |
| Value | 13 | exactly one | contains, is, gt, before, onOrAfter, has |
| List | 4 | a set | isAnyOf, isNoneOf, hasAnyOf, hasNoneOf |
| Range | 1 | two bounds | isBetween |
An operator belongs to a field kind, not to the API. A text field takes the text operators, a number field the numeric ones, and the sets barely overlap:
| Field kind | Operators it accepts |
|---|---|
text, longtext, email, url, phone, user, the record name |
the text set — contains, is, membership, emptiness |
decimal, currency, percent, formula |
the numeric set |
date, datetime, updated_at |
the date set, including the relative operators above |
select |
the select set |
multi_select |
has, hasAnyOf, hasNoneOf, isEmpty, isNotEmpty |
relation |
has, isEmpty, isNotEmpty |
boolean |
isTrue, isFalse, isEmpty |
address |
contains, isEmpty, isNotEmpty |
rollup, days_since |
none — they have no column to filter on |
A combination outside that table is refused with filter_unsupported_for_field, not ignored, so read the field’s kind before building a condition from user input.
Not every field can be sorted on
Sorting needs a total order the database can produce and a single value a cursor can carry. Five kinds have neither:
relation,multi_selectandaddresshold no one value to order by.rollupanddays_sincehave no column at all; the client computes them.
Everything else sorts. Ordering by one of those five is refused with order_field_unsupported, for the same reason they cannot appear in a cursor.