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:
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:
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 rowEDIT— update an existing related rowREMOVE— remove the relation onlyDELETE— 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:
personhas a 1:n relationaddresses- the target table is
address
ADD — Create new related row
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
addressrow is created - A relation between
personandaddressis created
ADD — Link existing row
Links an existing address without creating a new row.
Result:
- No new row is created
- The relation edge is created
EDIT — Edit related row
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.
Result:
- The relation is removed
- The address row still exists
DELETE — Remove relation and row
Removes both the relation and the related row.
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.