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.

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:
FieldTypeDescription
items[T!]!The records on the current page.
countInt!Total matching records — useful for pagination UIs.
pageInfo.hasNextPageBoolean!Whether more records exist after this page.
pageInfo.hasPreviousPageBoolean!Whether records exist before this page.
aggregatesJSONComputed 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.
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.
OperatorDescriptionExample
equalsExact match{ status: { equals: "active" } }
not_equalsNegated match{ status: { not_equals: "archived" } }
gtGreater than{ age: { gt: 18 } }
gteGreater than or equal{ age: { gte: 18 } }
ltLess than{ price: { lt: 100 } }
lteLess than or equal{ price: { lte: 100 } }
containsSubstring match{ name: { contains: "phone" } }
starts_withPrefix match{ sku: { starts_with: "PROD-" } }
is_emptyNULL or empty{ phoneNumber: { is_empty: true } }
is_not_emptyHas any value{ bio: { is_not_empty: true } }

Logical composition

AND, OR, and NOT compose any number of clauses. They can nest.
query {
  usersList(filter: {
    NOT: { status: { equals: "BANNED" } }
  }) {
    items { id email }
  }
}
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.
OperatorExample
today{ createdAt: { today: true } }
this_week{ createdAt: { this_week: true } }
this_month{ createdAt: { this_month: true } }
last_days{ createdAt: { last_days: 30 } }
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:
QuantifierMatches when…
someAt least one child matches the inner filter.
everyAll children match the inner filter (also matches parents with no children).
noneNo children match the inner filter.
query UsersWithHighRatedPosts {
  usersList(filter: {
    posts: { some: { rating: { gt: 5 } } }
  }) {
    items { id email }
  }
}
query UsersWithOnlyPublishedPosts {
  usersList(filter: {
    posts: { every: { published: { equals: true } } }
  }) {
    items { id }
  }
}
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:
OperationWhat it does
createInsert a new related record inline.
connectLink to an existing record by a unique field.
connectOrCreateFind 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.
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.

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:
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.
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 }
  }
}
PropertyAcceptsWhat it does
onDeleteCASCADE, SET NULL, RESTRICT, NO ACTIONWhat happens to child rows when the parent is deleted.
onUpdateSame setWhat happens to child rows when the parent’s key changes.
isNullabletrue / falseWhether the relationship column can be null. Required true for SET NULL.
descriptionStringDocumentation for the relationship.
For modeling cardinality and many-to-many patterns at the schema level, see 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. Authentication tokens come from App Services → Authentication Providers.

FAQ

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.
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.
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.
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.
Deep nesting works for typical use cases. For very deep filter trees, consider the _query POST endpoint on the REST API — it accepts a JSON body that’s easier to compose in code than a deeply nested GraphQL filter literal.