Skip to main content
A schema is the contract between your rows and everything else. The query parser resolves names against it, the executor reads values through it, the server validates requests with it, and the grid lays out its columns from it.
schema.ts
createSchema validates every column and throws a TallyError with code invalid_column for a malformed or duplicate column, and unknown_type for a type that is not registered.

Schema options

A key that is a field name can be read by servers. A key that is a function cannot, so tally-server, tally/laravel and the Supabase source require a field name. The grid’s newRow also needs a field name to generate keys, unless you pass your own newRow.

Column options

Only id is required. Everything else falls back to the column type. width, flex, pin and hidden are starting values. The view holds the current ones, so a user can resize, pin and hide columns without changing the schema.
Columns computed on the client, such as AI columns, must set filterable, sortable and groupable to false.

Resolving names

schema.resolve(name) finds a column by id, label or alias, ignoring case and surrounding spaces. The query language uses it, so MRR > 2k, mrr > 2k and an alias all name the same column.

Reading values

schema.valueOf(row, column) reads row[column.id]. schema.keyOf(row) reads the key and throws invalid_value when it is not a string or a number. schema.typeOf(column) returns the column type.

Extending a schema

schema.extend(columns, index?) returns a new schema with extra columns inserted at index, or at the end. The original schema is unchanged.

Trees

With parent, each row names its parent’s key, and rows with no parent are roots.
tree.ts
When a filter is applied to a tree, rows that match keep their ancestors, so a match is never shown without its path.