Skip to content
WrongNotebook
ProductsPersonal BaaSDocumentationQuickstart
Sign inCreate workspace
Browse documentation

Start here

  • Quickstart
  • API keys and scopes

Working with data

  • Collections
  • Records
  • Filtering, sorting and paging
  • Schemas and validation
  • Files

Integrate

  • Webhooks
  • Recipes

Reference

  • Errors
  • Limits and rate limits

Start here

  • Quickstart
  • API keys and scopes

Working with data

  • Collections
  • Records
  • Filtering, sorting and paging
  • Schemas and validation
  • Files

Integrate

  • Webhooks
  • Recipes

Reference

  • Errors
  • Limits and rate limits
  1. WrongNotebook
  2. /
  3. Docs
  4. /
  5. Filtering, sorting and paging

Filtering, sorting and paging

Query JSON records with a small filter language, sort on any field, search text and page through results with cursors.

Listing records takes query parameters. Everything here works on GET …/records and on public collections.

Filtering

filter takes a small expression. It reads close to how you would say it:

data.done == false
data.priority in ["high", "medium"]
data.tags contains "typescript"
data.title startsWith "Ship"
data.assignee.name == "Ayush"
createdAt >= "2026-01-01T00:00:00Z" and data.done == false
(data.a == 1 or data.b == 2) and not data.archived exists

Remember to URL-encode it. With curl, -G --data-urlencode does it for you:

Terminal
curl -s -G "…/records" -H "Authorization: Bearer $BAAS_KEY" \
  --data-urlencode 'filter=data.done == false' \
  --data-urlencode 'limit=20'
JavaScript
const query = new URLSearchParams({ filter: 'data.done == false', limit: '20' });
const res = await fetch(`${BASE}/workspaces/${WS}/collections/todos/records?${query}`, {
  headers: { Authorization: `Bearer ${KEY}` },
});

What you can compare

Your fieldsdata.name, data.profile.city, data.items[0].name, data["first name"]
Built-in fieldsid, createdAt, updatedAt, version, schemaVersion, createdBy, updatedBy
Values"text", numbers, true, false, null, lists like ["a","b"]
Joiningand, or, not, and parentheses

Operators

OperatorMeaning
==Equal
!=Not equal, or the field is missing
> >= < <=Numbers compare as numbers; text compares alphabetically (so ISO dates work)
in [...] / not in [...]Is (or is not) one of these
containsAn array contains this value
startsWithText begins with this
like / ilikeSQL-style patterns with %; ilike ignores case
exists / not existsThe field is present at all

A filter is parsed and turned into a safe database query, so text from your users cannot be used to attack the database. A filter the parser does not understand returns 400 INVALID_FILTER and tells you which character it stopped at.

Searching text

q searches text anywhere in the record; searchFields narrows it down:

Terminal
curl -s -G "…/records" -H "Authorization: Bearer $BAAS_KEY" \
  --data-urlencode 'q=invoice' \
  --data-urlencode 'searchFields=title,description'

It is a case-insensitive "contains", meant for a search box — not a ranked full-text engine.

Sorting

Terminal
--data-urlencode 'sort=-createdAt'     # newest first (the default)
--data-urlencode 'sort=data.priority'  # by one of your own fields

Put - in front for descending. You can sort by createdAt, updatedAt, version, id, or any data. path.

Paging

Results come back one page at a time with a cursor. Keep following nextCursor until it is null:

JavaScript
let cursor = null;
const all = [];
do {
  const query = new URLSearchParams({ limit: '100' });
  if (cursor) query.set('cursor', cursor);
  const res = await fetch(`${BASE}/workspaces/${WS}/collections/todos/records?${query}`, {
    headers: { Authorization: `Bearer ${KEY}` },
  });
  const page = await res.json();
  all.push(...page.data);
  cursor = page.pagination.nextCursor;
} while (cursor);

Cursors are safer than page numbers: no record is skipped or shown twice when rows are added while you page. A cursor belongs to the sort it was made with, so do not change sort halfway through.

Default page size is 50, maximum 200.

Fetching less

select returns only the fields you name, which keeps responses small:

Terminal
--data-urlencode 'select=title,done'

The built-in fields (id, version, timestamps) always come back.

Date ranges and the trash

ParameterMeaning
createdAfter, createdBeforeISO 8601 range on creation time
updatedAfter, updatedBeforeThe same for last change
deletedexclude (default), include, or only for the trash

Making queries fast

If you filter on the same field constantly, add an index for it in the collection's Indexes tab. Indexes are built in the background and help ==, in and range filters on that path. Five per collection.

Query limits

Filter length2,000 characters
Conditions per filter20
Nesting depth5
Values in one in [...]50
Depth of a field path8 segments
select fields25

Next steps

  • Records — writing the data you are querying.
  • Errors — what to do with 400 INVALID_FILTER.

View this page as Markdown — handy for copying into an editor or an AI assistant.

PreviousRecordsNextSchemas and validation

On this page

  • Filtering
  • Searching text
  • Sorting
  • Paging
  • Fetching less
  • Date ranges and the trash
  • Making queries fast
  • Query limits
  • Next steps
WrongNotebook

Personal BaaS is one focused product from WrongNotebook.

Documentation

  • All pages
  • Quickstart
  • Recipes
  • Errors

Product

  • Personal BaaS
  • All products
  • View the demo

Get started

  • Sign in
  • Create workspace

© 2026 WrongNotebook. All rights reserved.