Linked records (Joins)
Kottster allows you to fetch data from related tables when displaying data in a table or in a form for creating or updating records. To set this up, include the relationship configuration in the linked
object inside defineTableController
.
One-to-one
A one-to-one relationship links one record in a table to exactly one record in another table. To define this relationship in Kottster, provide the following object in linked
:
{
relation: 'oneToOne',
/** Foreign key column in the current table */
foreignKeyColumn: '',
/** The name of the target table */
targetTable: '',
/** The primary key column in the target table
that the foreign key refers to */
targetTableKeyColumn: '',
/** The array of columns in the target table to include
in queries and display by default */
columns: [],
/** The array of columns in the target table available for search */
searchableColumns: [],
}
Example
For example, consider a users
table with a workspace_id
column linking to a workspaces
table. Each user is assigned exactly one workspace.
Imagine we want to create a page to view and manage data in the users
table. By defining a one-to-one relationship with the workspaces
table, we can display detailed workspace information instead of just showing workspace_id
.
This also simplifies forms for creating or updating users. Instead of typing a workspace_id
, people can select a workspace from a dropdown or list, making the form more intuitive and reducing errors.
Here’s an example of the page file:
import { OneToOneRelation } from '@kottster/server';
import { TablePage } from '@kottster/react';
import { app } from '../.server/app';
import dataSource from '../.server/data-sources/postgres';
export const action =
app.defineTableController(dataSource, {
table: 'users',
primaryKeyColumn: 'id',
select: {
pageSize: 30,
},
insert: true,
update: true,
delete: true,
linked: {
user_workspace: new OneToOneRelation({
foreignKeyColumn: 'workspace_id',
targetTable: 'workspaces',
targetTableKeyColumn: 'id',
columns: ['id', 'name'],
searchableColumns: ['name'],
}),
}
});
export default () => (
<TablePage
columns={[
{
label: 'User ID',
column: 'id',
},
{
label: 'Name',
column: 'first_name',
},
{
label: 'Email',
column: 'email',
},
{
label: 'Workspace',
column: 'workspace',
linked: 'user_workspace',
},
]}
/>
);
As shown above, the page includes a linked
object with a relation under the key user_workspace. This key can have any name and is defined for convenience. It is also referenced in the Workspace column.
The relation we specified enables two features:
-
Displaying Data: Adds a Workspace column that shows the workspace ID and name from the
workspaces
table. -
Inserting/Updating Data: Allows people to select a workspace from a list, improving usability and reducing errors.
One-to-many
A one-to-many relationship links one record in a table to multiple records in another table. To define this relationship in Kottster, include the following object in linked
:
{
relation: 'oneToMany',
/** The name of the target table */
targetTable: '',
/** The primary key column in the target table */
targetTableKeyColumn: '',
/** The foreign key column in the target table
that refers to the current table */
targetTableForeignKeyColumn: '',
/** The array of columns in the target table to include
in queries and display by default */
columns: [],
/** The array of columns in the target table available for search */
searchableColumns: [],
}
Example
For example, consider a projects
table and a tasks
table, where each project can have multiple tasks.
Imagine we want to create a page to view data in the projects
table. By defining a one-to-many relationship, we can display an additional column showing how many tasks are associated with each project and their details.
Here’s how the page file might look:
import { OneToManyRelation } from '@kottster/server';
import { TablePage } from '@kottster/react';
import { app } from '../.server/app';
import dataSource from '../.server/data-sources/postgres';
export const action = app.defineTableController(dataSource, {
table: 'projects',
primaryKeyColumn: 'id',
select: {
pageSize: 30,
},
linked: {
project_tasks: new OneToManyRelation({
targetTable: 'tasks',
targetTableKeyColumn: 'id',
targetTableForeignKeyColumn: 'project_id',
columns: ['id', 'title', 'status'],
searchableColumns: ['title', 'status'],
}),
},
});
export default () => (
<TablePage
columns={[
{
label: 'Project Name',
column: 'name',
},
{
label: 'Tasks',
column: 'tasks',
linked: 'project_tasks',
},
]}
/>
);
Many-to-many
A many-to-many relationship links multiple records in one table to multiple records in another table. This is implemented using a junction table (also called a join table) to connect the two tables.
To define this relationship in Kottster, include the following object in linked
:
{
/** Specifies the type of relationship between tables */
relation: 'manyToMany';
/** Name of the table being referenced/joined */
targetTable: '',
/** The primary key column in the target table */
targetTableKeyColumn: '',
/** Name of the intermediate table that connects the source and target tables */
junctionTable: '',
/** Foreign key in the junction table referencing the source table */
junctionTableSourceKeyColumn: '',
/** Foreign key in the junction table referencing the target table */
junctionTableTargetKeyColumn: '',
/** The array of columns in the target table to include
in queries and display by default */
columns: [],
/** The array of columns in the target table available for search */
searchableColumns: []
}
Example
Consider an authors
table and a books
table, where an author can write multiple books, and a book can have multiple authors. To represent this many-to-many relationship, we use a junction table called author_books
, which links author_id
in the authors table to book_id
in the books table.
Imagine we want to create a page to view data in the books
table. By defining a many-to-many relationship with authors as the target table and author_books
as the junction table, we can add a column to show how many and which authors are associated with each book.
Here’s how the page file might look:
import { ManyToManyRelation } from '@kottster/server';
import { TablePage } from '@kottster/react';
import { app } from '../.server/app';
import dataSource from '../.server/data-sources/postgres';
export const action = app.defineTableController(dataSource, {
table: 'books',
primaryKeyColumn: 'id',
select: { pageSize: 30 },
linked: {
book_authors: new ManyToManyRelation({
// Junction table details
junctionTable: 'author_books',
junctionTableSourceKeyColumn: 'book_id',
junctionTableTargetKeyColumn: 'author_id',
// Target table details
targetTable: 'authors',
targetTableKeyColumn: 'id',
columns: ['id', 'full_name'],
searchableColumns: ['full_name'],
}),
},
});
export default () => (
<TablePage
columns={[
{
label: 'Book Title',
column: 'title'
},
{
label: 'Authors',
column: 'authors',
linked: 'book_authors'
},
]}
/>
);