Skip to main content

Documentation Index

Fetch the complete documentation index at: https://archie.com/docs/llms.txt

Use this file to discover all available pages before exploring further.

Every table in the Data Model generates a set of queries on the GraphQL schema. This page covers the patterns you’ll reach for most often. The examples assume a students table with fields like firstName, email, age, isActive, createdAt, and a city relationship.

Single record

Fetch one record by its primary key.
query {
  studentsById(id: "e25c4955-7ea7-4a83-a3de-d4c7427cc9fa") {
    id
    firstName
    email
  }
}
{
  "data": {
    "studentsById": {
      "id": "e25c4955-7ea7-4a83-a3de-d4c7427cc9fa",
      "firstName": "Robert",
      "email": "robert.williams@example.com"
    }
  }
}
Any field marked Unique in the Data Model can also be used as a query argument.

Record list

The list query wraps results in a Connection type with two top-level fields: items (the array of records) and count (the number of records returned).
query {
  students {
    count
    items {
      id
      firstName
      email
    }
  }
}

Filtered list

The filter argument takes a structured input that mirrors the field types on the table. Each field exposes a set of operators (equals, gt, contains, starts_with, etc.).
query {
  students(
    filter: {
      createdAt: { gt: "2025-12-01T00:00:00.000Z" }
      email: { contains: "williams" }
    }
  ) {
    items {
      id
      firstName
      email
    }
  }
}
For the full operator reference and the relational filter quantifiers (some, every, none), see Standard operations.

Combining conditions with AND / OR

Use the AND and OR keys to compose multiple conditions on the same query.
query {
  students(
    filter: {
      AND: {
        email: { contains: "johnson" }
        isActive: { equals: true }
      }
    }
  ) {
    items {
      id
      firstName
      email
      isActive
    }
  }
}
query {
  students(
    filter: {
      OR: {
        firstName: { equals: "Mary" }
        lastName: { equals: "Williams" }
      }
    }
  ) {
    items { id firstName lastName }
  }
}

Paginated list

first controls the page size; skip controls the offset.
query {
  students(skip: 0, first: 3) {
    items {
      id
      firstName
      email
    }
  }
}
For cursor-stable pagination, use pageInfo.hasNextPage and pageInfo.hasPreviousPage on the Connection wrapper:
query {
  students(first: 20) {
    pageInfo { hasNextPage hasPreviousPage }
    items { id firstName }
  }
}

Sorted list

Pass an array of field enums to sort. Append _DESC for descending; the bare enum sorts ascending.
query {
  students(sort: [CREATEDAT]) {
    items { id createdAt firstName }
  }
}
The object-style alternative is orderBy, which accepts ASC or DESC per field:
query {
  students(orderBy: { firstName: ASC }) {
    items { id firstName }
  }
}
For sorting by a computed aggregate value, use aggregateSort — see Grouping and aggregation.

Combining arguments

filter, first, skip, sort, and orderBy all stack on the same query.
query {
  students(
    filter: { isActive: { equals: true } }
    first: 3
    orderBy: { age: DESC }
  ) {
    items { id firstName age }
  }
}

Combining queries

A single GraphQL request can run multiple top-level queries. They execute in parallel and return as one response.
query {
  students(filter: { firstName: { equals: "James" } }) {
    count
    items { id firstName }
  }

  citiesById(id: "e14638cb-6d72-4a36-b30f-9b763136a7bb") {
    id
    nameCity
  }
}
If you need the same query run twice with different arguments in one request, use aliases:
query {
  cityOne: citiesById(id: "e14638cb-6d72-4a36-b30f-9b763136a7bb") {
    nameCity
  }
  cityTwo: citiesById(id: "0174dc55-d494-4ebc-a0e9-13575461cad4") {
    nameCity
  }
}

Aggregation

Every list query exposes a built-in count field on the Connection wrapper. No extra arguments needed.
query {
  students {
    count
    items { id firstName }
  }
}
For sums, averages, and other aggregate functions, see Grouping and aggregation below.

Grouping and aggregation

Four arguments work together to compute analytics in a single query:
ArgumentPurpose
groupByFields to group by (one row per distinct combination of values).
aggregateByAggregate functions to compute on each group.
havingFilter groups by aggregate results (SQL HAVING equivalent).
aggregateSortSort groups by aggregate values.

Group by a field

query {
  students(groupBy: [ISACTIVE]) {
    items { isActive }
    count
  }
}
The enum value is the camelCase field name converted to UPPERCASE: isActiveISACTIVE, paymentMethodPAYMENTMETHOD.

Compute aggregates per group

aggregateBy takes a list of { function, field, alias } entries. The result lands in the aggregates array, parallel to items.
query {
  students(
    groupBy: [ISACTIVE]
    aggregateBy: [
      { function: COUNT, alias: "totalStudents" }
      { function: AVG, field: AGE, alias: "avgAge" }
    ]
  ) {
    items { isActive }
    count
    aggregates
  }
}
{
  "data": {
    "students": {
      "items": [{ "isActive": true }, { "isActive": false }],
      "count": 2,
      "aggregates": [
        { "totalStudents": 5, "avgAge": 23.4 },
        { "totalStudents": 2, "avgAge": 21.0 }
      ]
    }
  }
}
FunctionField required?
COUNTNo (counts all rows when omitted)
SUMYes
AVGYes
MINYes
MAXYes
COUNT_DISTINCTYes

Filter groups with having

having references the alias defined in aggregateBy. Operators are EQUALS, NOT_EQUALS, GREATER_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN, LESS_THAN_OR_EQUAL.
query {
  students(
    groupBy: [CITYID]
    aggregateBy: [{ function: COUNT, alias: "studentCount" }]
    having: [{ alias: "studentCount", operator: GREATER_THAN, value: 3 }]
  ) {
    items { cityId }
    aggregates
  }
}

Sort by aggregate

query {
  students(
    groupBy: [CITYID]
    aggregateBy: [{ function: COUNT, alias: "studentCount" }]
    aggregateSort: [{ alias: "studentCount", direction: DESC }]
    first: 3
  ) {
    items { cityId }
    aggregates
  }
}

All four together

query {
  students(
    filter: { isActive: { equals: true } }
    groupBy: [CITYID]
    aggregateBy: [
      { function: COUNT, alias: "studentCount" }
      { function: AVG, field: AGE, alias: "avgAge" }
    ]
    having: [{ alias: "avgAge", operator: GREATER_THAN, value: 20 }]
    aggregateSort: [{ alias: "avgAge", direction: DESC }]
    first: 5
  ) {
    items { cityId }
    aggregates
  }
}
filter runs before grouping (SQL WHERE); having runs after (SQL HAVING).

Permissions

Every query is filtered by the per-role permissions in Role-Based Access. Records and fields a role isn’t allowed to read are silently omitted from the response — they don’t trigger errors.

FAQ

count reflects the total number of matching records (subject to permissions), while items reflects only the current page. With first: 10, items.length is at most 10 but count may be much larger.
They do the same thing in different syntaxes. sort takes an array of enum values ([CREATEDAT, FIRSTNAME_DESC]); orderBy takes an object ({ createdAt: ASC, firstName: DESC }). Pick whichever reads better in your query.
The Explorer accepts large first values, but very large pages are slow. Use pagination with reasonable page sizes (20–100) and combine with sorting to keep results stable across requests.
Yes — switch environments in the Backend Console, or call the environment-specific endpoint from outside the Explorer. See Environments.