Skip to content

DSL reference

app.ts
import { defineApp, listView } from 'tablewalk/app';
export default defineApp({
name: 'Compass CRM',
connection: 'compass',
resources: { opportunity: {} },
views: { opportunities: listView('Opportunities', 'opportunity') },
home: 'opportunities',
nav: ['opportunities'],
});

An App directory holds exactly one app.ts, app.mjs or app.json. The definition is plain data. Helpers such as defineApp, listView, page, action and metric return ordinary objects and keep your literals, so a column a helper names is still type-checked. A view, a list presentation, a section, a page and a resource action are written with their helper only; an object written by hand in their place is a type error that names the helper. Every place takes a fixed set of keys, and an unknown key is refused at load with the nearest spelling (Did you mean "label"?). The complete types are schema/app.d.ts, which ships in the package.

The DSL in one App builds one step by step, and Choose a building block says which key a task wants.

listView('Deals', 'opportunity', { query: 'stage = won' })
// Type '"stage = won"' is not assignable to type
// '{ "won is not a value of stage: prospecting, …": true; }'.

A view’s or tab’s query and sort, a dashboard’s or page’s query and by, a rollup’s query, a command’s when and a reference field’s picker query are read against the table’s columns as you type them, once the App’s generated tablewalk-schema.d.ts registers its Columns. A misspelled column, an operator the language does not have, or a value outside a column’s vocabulary is a type error on the string.

The limit: the strings are checked only when the TypeScript project holds exactly one registered schema. With none (no generated file) or with two or more (a test or a monorepo root that reaches two Apps’ files), the checking stands down rather than judge one App by another’s columns, and tablewalk check alone judges the strings when the App loads. The guard reads conditions, operators, measures, group-bys and sorts two references deep; what it cannot read for certain (a quoted value, between, a third hop) passes to check.

Terminal window
npx tablewalk check --app . --config tablewalk.json # types, typecheck, validation
npx tablewalk check --app . --config tablewalk.json --brief # one line per problem
npx tablewalk explain unknown-column # one code, with its fix

satisfies App, with the generated type, checks table, column, connection, view and look names in the editor, and label paths hop by hop for two hops. A column no reader is served (hidden, or outside every role’s fields) is generated apart, as Hidden: hidden, fields and audit.redact take it, and a place that shows, sorts or writes a column does not. check regenerates the types, typechecks, validates against the database and warns [restated] where the App says what the platform already knows. Every code it prints is on tablewalk check.

Module graph. Split an App into app.ts (identity, sources, auth, ui), resources.ts, views.ts, navigation.ts and pages/. Modules run as trusted server code in a fresh worker, not a sandbox; a broken edit keeps the last good definition. Command handlers stay out of this graph. The worker reads only the environment variables tablewalk.json lists for the App (secrets).

Part What it holds
root keys The keys at the top of an App that name it, say where its data comes from and where it opens.
resources What each table is called, which fields it shows, and the actions, forms, access and rules that belong to it.
views Everything a visitor can open: lists, dashboards, forms and the rest, keyed by address.
pages Record pages: how one record is laid out, section by section.
auth Who signs in, which roles exist, and what each role may read and change.
ui and branding The look, the shell, the footer and the density of the App, and what each tenant may brand.
api The App-shaped HTTP API an App can opt into.
flags, experiments and analytics Feature flags, experiments and the events an App records about its own use.
features A slice of an App that can be switched on together: it restates the App’s own content keys.
shared shapes Shapes that many keys take, listed once here: a message that may be translated, a link target and a component declaration. A key that takes one says = <name>.