> ## 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.

# Standard operations

> Filters, relationship traversal, nested mutations, and the standard patterns shared across every query and mutation in the schema.

Most queries and mutations on the GraphQL API share a common shape — the same filter operators, the same Connection wrapper around lists, the same nested-mutation conventions. This page is the reference for those shared patterns.

## The Connection wrapper

Every list field in the schema (root-level lists and nested relationship lists) returns a Connection wrapper:

| Field | Type | Description |
| - | - | - |
| `items` | `[T!]!` | The records on the current page. |
| `count` | `Int!` | Total matching records — useful for pagination UIs. |
| `pageInfo.hasNextPage` | `Boolean!` | Whether more records exist after this page. |
| `pageInfo.hasPreviousPage` | `Boolean!` | Whether records exist before this page. |
| `aggregates` | `JSON` | Computed aggregate values when `aggregateBy` is used. |

Because the wrapper is consistent at every depth, you can pass `filter`, `first`, `skip`, and `orderBy` to a nested list the same way you pass them at the root.

```graphql theme={null}
query {
  categoriesList {
    items {
      name
      productList(
        filter: { isFeatured: { equals: true } }
        first: 3
        orderBy: { stock: DESC }
      ) {
        count
        pageInfo { hasNextPage }
        items { id name stock }
      }
    }
  }
}
```

## Filter operators

### Comparison and text

Most fields support a standard set of operators. The exact set depends on the field type — text fields get string operators, numeric fields get range operators, and so on.

| Operator | Description | Example |
| - | - | - |
| `equals` | Exact match | `{ status: { equals: "active" } }` |
| `not_equals` | Negated match | `{ status: { not_equals: "archived" } }` |
| `gt` | Greater than | `{ age: { gt: 18 } }` |
| `gte` | Greater than or equal | `{ age: { gte: 18 } }` |
| `lt` | Less than | `{ price: { lt: 100 } }` |
| `lte` | Less than or equal | `{ price: { lte: 100 } }` |
| `contains` | Substring match | `{ name: { contains: "phone" } }` |
| `starts_with` | Prefix match | `{ sku: { starts_with: "PROD-" } }` |
| `is_empty` | NULL or empty | `{ phoneNumber: { is_empty: true } }` |
| `is_not_empty` | Has any value | `{ bio: { is_not_empty: true } }` |

### Logical composition

`AND`, `OR`, and `NOT` compose any number of clauses. They can nest.

```graphql theme={null}
query {
  usersList(filter: {
    NOT: { status: { equals: "BANNED" } }
  }) {
    items { id email }
  }
}
```

```graphql theme={null}
query {
  ordersList(filter: {
    OR: [
      { status: { equals: "completed" } }
      {
        AND: [
          { status: { equals: "pending" } }
          { createdAt: { last_days: 7 } }
        ]
      }
    ]
  }) { items { id status } }
}
```

### Date helpers

Relative date filters save you from computing exact timestamps.

| Operator | Example |
| - | - |
| `today` | `{ createdAt: { today: true } }` |
| `this_week` | `{ createdAt: { this_week: true } }` |
| `this_month` | `{ createdAt: { this_month: true } }` |
| `last_days` | `{ createdAt: { last_days: 30 } }` |

```graphql theme={null}
query {
  logsList(filter: { timestamp: { last_days: 7 } }) {
    items { message }
  }
}
```

### Relational quantifiers

To filter parent records by the state of their related children, use `some`, `every`, and `none`:

| Quantifier | Matches when... |
| - | - |
| `some` | At least one child matches the inner filter. |
| `every` | All children match the inner filter (also matches parents with no children). |
| `none` | No children match the inner filter. |

```graphql theme={null}
query UsersWithHighRatedPosts {
  usersList(filter: {
    posts: { some: { rating: { gt: 5 } } }
  }) {
    items { id email }
  }
}
```

```graphql theme={null}
query UsersWithOnlyPublishedPosts {
  usersList(filter: {
    posts: { every: { published: { equals: true } } }
  }) {
    items { id }
  }
}
```

```graphql theme={null}
query UsersWithNoDrafts {
  usersList(filter: {
    posts: { none: { status: { equals: "DRAFT" } } }
  }) {
    items { id }
  }
}
```

## Nested mutations

Every relationship field on a mutation input accepts exactly one of three operations:

| Operation | What it does |
| - | - |
| `create` | Insert a new related record inline. |
| `connect` | Link to an existing record by a unique field. |
| `connectOrCreate` | Find by a unique field; create if missing. |

The whole nested mutation runs as a single transaction. If any step fails, the entire operation rolls back.

```graphql theme={null}
mutation {
  createDeal(input: {
    title: "Enterprise License"
    amount: 50000

    contact: { create: { firstName: "Juan", email: "juan@acme.com" } }
    user: { connect: { id: "user-uuid-123" } }
    organization: {
      connectOrCreate: {
        where: { domain: "acme.com" }
        create: { name: "Acme Corp", domain: "acme.com" }
      }
    }
  }) {
    id
    contact { id }
    organization { id }
  }
}
```

For the full mutations reference, see [Mutations](/docs/features/backend/graphql-api-explorer/mutations).

## Atomic upserts

Tables with at least one unique column expose an `upsert<TableName>` mutation. It atomically checks the unique field and runs `create` or `update`:

```graphql theme={null}
mutation {
  upsertContact(
    where: { email: "juan@example.com" }
    create: {
      firstName: "Juan"
      email: "juan@example.com"
      status: "LEAD"
    }
    update: {
      status: "ACTIVE"
    }
  ) {
    id status
  }
}
```

Use upsert in place of "select then create or update" to eliminate the race condition.

## Modifying relationships

The `updateRelationship` mutation changes how a foreign key behaves — cascade behavior, nullability, descriptive metadata. Names map to the underlying database constraint name.

```graphql theme={null}
mutation EnableCascade {
  updateRelationship(
    tableName: "organization_user"
    relationship: {
      name: "organization_user_users_id_fk"
      onDelete: "CASCADE"
      description: "Reference to the user belonging to the organization"
    }
  ) {
    name
    relationships { name onDelete fromColumn }
  }
}
```

| Property | Accepts | What it does |
| - | - | - |
| `onDelete` | `CASCADE`, `SET NULL`, `RESTRICT`, `NO ACTION` | What happens to child rows when the parent is deleted. |
| `onUpdate` | Same set | What happens to child rows when the parent's key changes. |
| `isNullable` | `true` / `false` | Whether the relationship column can be null. Required `true` for `SET NULL`. |
| `description` | String | Documentation for the relationship. |

For modeling cardinality and many-to-many patterns at the schema level, see [Relationships](/docs/features/backend/data-model/relationships).

## Permissions and authentication

Filters, nested mutations, and upserts all run through the same authorization checks. Read access is required for filter clauses that touch a related table; write access is required for every step of a nested mutation. See [Role-Based Access](/docs/features/backend/app-services/role-based-access). Authentication tokens come from [App Services → Authentication Providers](/docs/features/backend/app-services/authentication-providers/overview).

## FAQ

<AccordionGroup>
  <Accordion title="When should I use `every` vs `some`?">
    `some` is "at least one child matches" — finds parents that have any matching child. `every` is stricter — all children must match. Note that `every` also matches parents with **zero** children, which is sometimes surprising.
  </Accordion>

  <Accordion title="What's the difference between `connect` and passing a foreign key id directly?">
    `connect` is the schema-level way to link by a unique field — including non-id columns like an email. Passing a raw foreign key id is shorter, but only works when you already have the id and the column is the primary key.
  </Accordion>

  <Accordion title="Are nested mutations slower than separate operations?">
    Faster, in practice. They run in one round-trip and one database transaction, where separate operations require a round-trip per step and don't share a transaction. They also avoid orphaned records when a later step fails.
  </Accordion>

  <Accordion title="Can I combine `having` with `filter`?">
    Yes. `filter` runs before grouping (SQL `WHERE`); `having` runs after (SQL `HAVING`). Use `filter` to scope the rows that participate in the aggregation, and `having` to drop groups whose aggregate doesn't meet a threshold. See [Queries → Grouping and aggregation](/docs/features/backend/graphql-api-explorer/queries#grouping-and-aggregation).
  </Accordion>

  <Accordion title="What's the maximum nesting depth on filters?">
    Deep nesting works for typical use cases. For very deep filter trees, consider the `_query` POST endpoint on the [REST API](/docs/features/backend/rest-api-explorer/queries) — it accepts a JSON body that's easier to compose in code than a deeply nested GraphQL filter literal.
  </Accordion>
</AccordionGroup>
