Model and read relationships
Relationships connect focused tables without duplicating their data. TaruviBase derives relationship metadata from the schema's foreign keys.
The patterns below use flat tables. Whole-table JSONB storage has a separate relationship compatibility boundary.
| Shape | Example | Model |
|---|---|---|
| Belongs to | A task belongs to one project | tasks.project_id references projects.id |
| Has many | A project has many tasks | Reverse view of tasks.project_id |
| Many to many | A task can have many tags | A junction table references both tables |
Define a belongs-to relationship
Create the referenced table first. Then add the foreign-key field and
foreignKeys definition to the table that owns the reference. For example,
add project_id to tasks and reference projects.id.
Use the logical table name in reference.resource. NO ACTION keeps deletion
explicit: remove or redirect referencing tasks before deleting their project.
Use the foreign-key declaration in the schema
reference for
the exact descriptor and supported delete actions.
Model many-to-many data
Create a junction table when both sides can have many records. For tasks and
tags, define task_tags with:
- its own stable
id; task_idreferencingtasks.id; andtag_idreferencingtags.id.
Create, query, and delete junction records through the ordinary record
workflows. This makes each relationship explicit and gives it room for fields
such as created_at or assigned_by.
Include related records
Add populate to a list or read to return related records with each result.
Use the foreign-key field for a belongs-to relationship, such as
populate=project_id on tasks, or the related table's name for the reverse
direction, such as populate=tasks on projects. populate=* includes every
first-level relationship. In the SDKs, use .populate(['project_id']) in
JavaScript or .populate("project_id") in Python.
TaruviBase checks that the caller can read each related table. populate can't be
combined with aggregation queries.
Read related data
To page through related records on their own, or to combine data your own way, read each table in its own request:
- Read the root records from their table.
- Collect the foreign-key values needed by the application.
- Query the related table with the same site, app, and caller context.
- Join the records in application code.
This keeps response size, pagination, and authorization visible at each step.
For example, after reading tasks and collecting their project_id values into
projectIds, fetch the related projects with one bounded query:
- JavaScript SDK
- Python SDK
- Refine
- REST API
await database
.from('projects')
.filters('id', 'in', projectIds)
.sort('name', 'asc')
.sort('id', 'asc')
.page(1)
.pageSize(20)
.execute();
(
client.database
.from_("projects")
.filter("id", "in", project_ids)
.sort("name", "asc")
.sort("id", "asc")
.page(1)
.page_size(20)
.execute()
)
useList({
resource: 'projects',
filters: [{field: 'id', operator: 'in', value: projectIds}],
sorters: [
{field: 'name', order: 'asc'},
{field: 'id', order: 'asc'},
],
pagination: {currentPage: 1, pageSize: 20, mode: 'server'},
});
/api/apps/$TARUVI_APP_SLUG/datatables/projects/data/curl -G "$TARUVI_SITE_URL/api/apps/$TARUVI_APP_SLUG/datatables/projects/data/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
--data-urlencode "id__in=$PROJECT_IDS" \
--data-urlencode "ordering=name,id" \
--data-urlencode "page=1" \
--data-urlencode "page_size=20"
200Returns the related projects in their own app-scoped collection response.
Set PROJECT_IDS to the comma-separated project UUIDs collected from the task response.
Build a map keyed by project ID, then attach each project to its task in
application code. Apply the same pattern in the other direction by filtering
tasks.project_id with the selected project IDs.
Choose a query pattern
| Requirement | Pattern |
|---|---|
| Read a foreign-key value | Read the root record normally |
| Read the referenced record | Query the referenced table by ID |
| Read a has-many collection | Filter the child table by its foreign-key field |
| Read a many-to-many collection | Query the junction table, then query the target IDs |
| Filter or sort by related data | Query the related IDs first, then filter and sort the root table |
| Show related rows with root rows | Use populate, or read both tables and join them in application code |
Separate table reads give you independent pagination and response size for each table.
Relationship checklist
- Create the referenced table before the referencing table.
- Use compatible field types on both sides.
- Choose delete behavior deliberately.
- Test valid and missing references.
- Apply pagination and stable ordering to each collection read.
- Use
populatefor small related sets, and separate paged reads for large ones.
Next, use aggregations to summarize the records returned by a table query.