Abrova
☰ Docs menu · Queries
Coming soon. Abrova Sync is coming soon. This page describes how it will work; it can’t be used yet.

Querying Data

Filter, sort, and paginate Abrova Sync documents with a fluent query API. Comparison operators, array-contains, IN, plus ordering and offset pagination — across Flutter, React Native, Web, Android, iOS, Node.js, and REST API.

Last updated: 27 Oct 2026

A query is a recipe for narrowing down a collection: "give me users where age is at least 18, ordered by signup date, limit 20". The SDK builds the query on the client, the backend evaluates it, and you get back a result list. Queries also work as inputs to realtime listeners — same fluent shape.

where()

Filter rows by field. Chain multiple where() calls to AND them.

orderBy()

Sort by one field, ascending or descending. A second orderBy replaces the first.

limit() + offset()

Page through large result sets without loading everything.

Available operators#

OperatorSymbol / EnumDescription
Equal==Field equals value
Not Equal!=Field does not equal value
Greater Than>Field strictly greater than value (numeric fields only)
Greater Than or Equal>=Field greater than or equal to value (numeric fields only)
Less Than<Field strictly less than value (numeric fields only)
Less Than or Equal<=Field less than or equal to value (numeric fields only)
Array Containsarray-containsArray field contains the value (matched as text, so a partial match also counts)
IninField value is in the given array
Not Innot-inNot supported: the filter is ignored

Good to know: two where() calls on the same field with different operators make a range (age >= 18 and age < 65); the same operator twice on one field is refused. Sorting puts numbers in number order, then text, and documents without the field last (createdAt / updatedAt sort by the document timestamps). A query returns at most 1000 documents (100 when you set no limit).

Queries and your security rules#

A query must match your security rules: it is allowed only when the rules allow every document it could return, otherwise it fails as a whole with 403 query_not_allowed. The error names the fields the rules read — filter on them with == (or in). For example, with a rule data.ownerId === auth.uid, query where('ownerId', '==', <the user's id>). Every SDK reports it as a typed error with those fields, from get() and from listeners.

When the rules allow a query because of its == / in filters, those filters compare by type: 5 matches 5, not "5". Send values with their real type.

A query on a collection that has no documents yet is checked against the rules like any other query. When the rules allow it, it returns an empty list. When they don't, it fails with query_not_allowed, even though there is nothing to return.

The errors and how to catch them on each platform are in Platform Notes.

Flutter#

dart
// Build queries with fluent API
final adults = await users
    .query()
    .where('age', QueryOperator.greaterThanOrEqual, 18)
    .where('status', QueryOperator.equal, 'active')
    .orderBy('createdAt', direction: OrderDirection.descending)
    .limit(20)
    .get();

// Available operators
QueryOperator.equal              // ==
QueryOperator.notEqual           // !=
QueryOperator.greaterThan        // >
QueryOperator.greaterThanOrEqual // >=
QueryOperator.lessThan           // <
QueryOperator.lessThanOrEqual    // <=
QueryOperator.arrayContains      // array contains value
QueryOperator.isIn               // value in array
QueryOperator.notIn              // value not in array (not supported)

React Native#

typescript
// Build queries with fluent API
const adults = await users
    .query()
    .where('age', '>=', 18)
    .where('status', '==', 'active')
    .orderBy('createdAt', 'desc')
    .limit(20)
    .get();

// Available operators: '==', '!=', '>', '>=', '<', '<=', 'array-contains', 'in', 'not-in'

Web#

typescript
// Build queries with fluent API
const adults = await users
    .where('age', '>=', 18)
    .where('status', '==', 'active')
    .orderBy('createdAt', 'desc')
    .limit(20)
    .get();

// Available operators: '==', '!=', '>', '>=', '<', '<=', 'array-contains', 'in', 'not-in'

Android#

kotlin
// Build queries with fluent API
lifecycleScope.launch {
    val adults = users
        .query()
        .where("age", QueryOperator.GREATER_THAN_OR_EQUAL, 18)
        .where("status", QueryOperator.EQUAL, "active")
        .orderBy("createdAt", OrderDirection.DESCENDING)
        .limit(20)
        .get()
}

// Available operators
QueryOperator.EQUAL                 // ==
QueryOperator.NOT_EQUAL             // !=
QueryOperator.GREATER_THAN          // >
QueryOperator.GREATER_THAN_OR_EQUAL // >=
QueryOperator.LESS_THAN             // <
QueryOperator.LESS_THAN_OR_EQUAL    // <=
QueryOperator.ARRAY_CONTAINS        // array contains
QueryOperator.IN                    // value in array
QueryOperator.NOT_IN                // value not in array (not supported)

iOS#

swift
// Build queries with fluent API
Task {
    let adults = try await users
        .query()
        .where("age", .greaterThanOrEqual, 18)
        .where("status", .equal, "active")
        .orderBy("createdAt", direction: .descending)
        .limit(20)
        .get()
}

// Available operators: .equal, .notEqual, .greaterThan, .greaterThanOrEqual,
//                      .lessThan, .lessThanOrEqual, .arrayContains, .in, .notIn

Node.js#

typescript
// Build queries with fluent API
const adults = await users
    .where('age', '>=', 18)
    .where('status', '==', 'active')
    .orderBy('createdAt', 'desc')
    .limit(20)
    .get();

// Compound queries
const recentActiveUsers = await users
    .where('status', '==', 'active')
    .where('lastLogin', '>=', thirtyDaysAgo)
    .orderBy('lastLogin', 'desc')
    .get();

// Pagination with offset
const page1 = await users.orderBy('name').limit(10).get();
const page2 = await users.orderBy('name').limit(10).offset(10).get();
const page3 = await users.orderBy('name').limit(10).offset(20).get();

REST API#

bash
# Query documents: POST the same shape the SDKs send
curl -X POST "https://sync.abrova.ir/databases/{databaseName}/collections/{collectionName}/documents/sdk/query" \
  -H "x-api-key: YOUR_PROJECT_API_KEY" \
  -H "x-server-secret: YOUR_SERVER_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": [
      { "field": "age", "operator": ">=", "value": 18 },
      { "field": "status", "operator": "==", "value": "active" }
    ],
    "orderBy": { "field": "createdAt", "direction": "desc" },
    "limit": 20,
    "offset": 40
  }'
# Response: { "data": [...], "total": 123 }
# Operators: ==, !=, >, >=, <, <=, array-contains, in (not-in is not supported)

# Or GET with a JSON filter and sort (MongoDB-style operators)
curl -G "https://sync.abrova.ir/databases/{databaseName}/collections/{collectionName}/documents/sdk" \
  --data-urlencode 'filter={"age":{"$gte":18},"status":"active"}' \
  --data-urlencode 'sort={"createdAt":-1}' \
  --data-urlencode 'limit=20' \
  --data-urlencode 'skip=40' \
  -H "x-api-key: YOUR_PROJECT_API_KEY" \
  -H "x-server-secret: YOUR_SERVER_SECRET"
# Response: { "data": [...], "total": 123, "skip": 40, "limit": 20 }
# Filter operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $contains

Next: keep a UI in sync with Realtime Listeners.

Was this page helpful?
Report a problem