Skip to content

API relations

Relations represent structured connections between rows defined in the schema.

In the API, relations appear as fields on GraphQL types. These fields allow clients to:

  • traverse related records in queries
  • modify relations through nested mutation inputs

Relation behavior in the API directly reflects the underlying schema configuration and follows the same validation, permission, and rule enforcement as other operations.

Relation representation

Relations are exposed as named fields on schema types.

The relation field name corresponds to the relation API name configured in the schema. The cardinality of the relation determines whether the field returns:

  • a single row
  • a list of rows

Example:

If person has a relation addresses, the relation can be queried like this:

query {
  persons {
    id
    first_name
    addresses {
      id
      city
    }
  }
}

Returned rows are filtered by read rules. If a related row is not visible to the authenticated user, it will not appear in the result.

Traversing relations

Relations can be traversed by selecting nested fields in a query.

Traversal behavior:

  • follows schema-defined relation direction
  • supports multiple nested levels
  • enforces read rules on every table involved

Example:

query {
  persons {
    id
    first_name
    addresses {
      id
      city
    }
  }
}

If a related address is hidden by read rules, it is omitted from the response.

Editing relations

Relations are edited through nested mutation inputs.

Each related item specifies an _action field describing the operation to perform.

Relation actions

The following actions are supported:

  • ADD — create a relation and optionally create a new target row
  • EDIT — update an existing related row
  • REMOVE — remove the relation only
  • DELETE — remove both the relation and the related row

All relation edits participate in the parent mutation transaction.

Write rules are evaluated for both the source row and the related rows.

Examples

Assume:

  • person has a 1:n relation addresses
  • the target table is address

Creates a new address and links it to the person.

mutation {
  edit_person(id: 123, input: {
    addresses: [
      {
        _action: ADD
        street: "Main Street 1"
        city: "Stockholm"
      }
    ]
  }) {
    id
  }
}

Result:

  • A new address row is created
  • A relation between person and address is created

Links an existing address without creating a new row.

mutation {
  edit_person(id: 123, input: {
    addresses: [
      {
        _action: ADD
        id: 987
      }
    ]
  })
}

Result:

  • No new row is created
  • The relation edge is created

Updates a related address row.

mutation {
  edit_person(id: 123, input: {
    addresses: [
      {
        _action: EDIT
        id: 987
        city: "Gothenburg"
      }
    ]
  })
}

Result:

  • The address row is updated
  • The relation remains unchanged

REMOVE — Remove relation only

Removes the relation but keeps the related row.

mutation {
  edit_person(id: 123, input: {
    addresses: [
      {
        _action: REMOVE
        id: 987
      }
    ]
  })
}

Result:

  • The relation is removed
  • The address row still exists

DELETE — Remove relation and row

Removes both the relation and the related row.

mutation {
  edit_person(id: 123, input: {
    addresses: [
      {
        _action: DELETE
        id: 987
      }
    ]
  })
}

Result:

  • The relation is removed
  • The address row is deleted

Constraints

Relation operations follow the same constraints as other API mutations:

  • Cardinality rules defined in the schema are enforced
  • Relation edits participate in the parent mutation transaction
  • Write rules are evaluated during execution

Invalid relation names, identifiers, or rule violations cause the mutation to fail and no partial changes are committed.

Related concepts