Skip to main content

Relations

Relations connect records from different entities using relation type fields.

Create a Relation

When defining a relation type field, the related entity is specified:

generate_entity({
definition: {
entity: { name: "facturas" },
fields: [
{
name: "Cliente",
fieldKey: "cliente_id",
fieldType: "relation",
config: { relatedEntity: "clientes" }
}
]
}
})

Storage

The relation field stores the UUID of the related record:

{
"cliente_id": "6bd2d1db-d104-4a15-977a-a759c38608a9"
}

Resolving Relations

When querying records, relations can be expanded with resolve:

MCP

query_records resolves relations automatically (uses resolve=true internally).

REST API

GET /api/entities/facturas/records?resolve=true
GET /api/entities/facturas/records/{id}?resolve=true

Without resolve, the field shows only the UUID. With resolve, it expands to the full object:

Without resolve:

{
"cliente_id": "6bd2d1db-..."
}

With resolve:

{
"cliente_id": {
"id": "6bd2d1db-...",
"data": {
"nombre": "Juan Perez",
"email": "juan@example.com"
}
}
}

Create Records with Relations

When creating a record, pass the UUID of the related record:

create_record({
entityName: "facturas",
data: {
numero: "FAC-001",
cliente_id: "6bd2d1db-d104-4a15-977a-a759c38608a9",
fecha: "2026-02-18",
total: 1500
}
})

Has-Many Relations

A has_many field defines a reverse one-to-many relationship. Instead of storing a foreign key, it declares that another entity has records pointing back to this one.

Define a has_many field

generate_entity({
definition: {
entity: { name: "facturas" },
fields: [
{
name: "Lineas",
fieldKey: "lineas",
fieldType: "has_many",
config: {
relatedEntity: "lineas_factura",
foreignKey: "factura_id"
}
}
]
}
})

This declares: "each factura has many lineas_factura where lineas_factura.factura_id points to this factura."

Resolving has_many

When you query a record with resolve=true, the has_many field is populated with an array of related records:

GET /api/entities/facturas/records/{id}?resolve=true
{
"id": "uuid-factura",
"data": {
"numero": "FAC-001",
"lineas": [
{
"id": "uuid-linea-1",
"data": { "producto": "Widget A", "cantidad": 3, "precio": 100 }
},
{
"id": "uuid-linea-2",
"data": { "producto": "Widget B", "cantidad": 1, "precio": 250 }
}
]
}
}

Nested resolution with resolve_depth

Control how deep nested relations are resolved:

GET /api/entities/facturas/records/{id}?resolve=true&resolve_depth=2

With resolve_depth=2, if a linea_factura itself has a relation field pointing to productos, that relation is also resolved.

Permission-aware resolution

Cascading resolution respects RBAC permissions per entity:

PermissionBehavior
No read permission on related entityField is omitted entirely from the response
rowFilter on related entityOnly matching related records are returned
fields whitelist on related entityOnly whitelisted fields appear in related records
excludeFields blocklist on related entityListed fields are stripped from related records

This means different users can see different related data based on their role configuration.

Relations in Business Rules

Rules can use lookups to access data from related entities:

{
"type": "compute",
"triggers": ["cliente_id"],
"compute": {
"cliente_nombre": {
"type": "lookup",
"entity": "clientes",
"matchField": "id",
"matchValue": "cliente_id",
"resultField": "nombre"
}
}
}