Partitions
elements man livetable/partitions Read as markdownOne slice of a table per page: the comments for a post, the messages in a room, the tasks for a project.
let comments = new LiveTable<Comment>();
export default function route(req) {
return new html({ comments: comments.view({ postId: req.params.id }) });
}
The object passed to view() is a set of column equalities. One object does
four jobs:
- It names the channel. Each partition gets its own pub/sub channel, so a mutation in one post's comments only reaches browsers watching that post.
- It narrows the select. Auto-select adds
where post_id = $1; a custom select receives the object and writes the same clause. - It fills the columns on insert.
comments.insert({ text })getspostIdfrom the view. Passing a different value throws. - It is checked on every mutation. A row naming another partition is refused
with
PartitionMismatchError, before any handler runs and independent of anything the handler checks.
Several columns compose the same way, and the order you write them does not matter:
tasks.view({ projectId, status: "open" })
Only a column holding a string, number, or boolean can partition. The value has to become part of a channel name, a SQL parameter, and a string comparison on the browser.
Authorization lives at the call
Nothing about a partition is a permission. comments.view({ postId }) opens
that post's comments for whoever asked, so the route or RPC that calls it is
where the check goes, because that is the only place that knows who is asking:
export default function route(req) {
session.isLoggedInOrThrow();
return new html({ transfers: transfers.view({ userId: session.getOrThrow("userId") }) });
}
view() is server-only, and so is the LiveTable it is called on. The browser
receives the view already partitioned. There is no way for browser code to
widen it, because it never holds the declaration that could.
A row that changes partition
When an update moves a row from one partition to another, status going from
"open" to "closed" say, the server broadcasts a delete to the partition it
left and an insert to the one it joined. Any column may be a partition key, and
a view stays correct when rows move.
Whole table and partition together
A page on view() and a page on view({ roomId }) can both be open. A write
through either view broadcasts on the whole-table channel and on the channel
of every partition column set the table has been opened with, named by the
row's own values, so both pages hear it. That holds for a write through
view() on the server too, as long as this process has opened a partitioned
view of the table before. A freshly started process that has only ever served
view() reaches the whole-table channel alone, so when a server-side write
belongs to a room, make it through view({ roomId }).
Filtering beyond the partition
Everything narrower than a column equality is filter() in the template:
<li e:for={c of comments.filter((c) => !c.archived)}>
A custom select may also filter in SQL to keep the first render small, but
the stream does not know about that clause: a row broadcast on the partition's
channel reaches the browser whether or not the select would have returned it.
Keep the template's filter as the rule and treat the select's as an
optimization of the snapshot. For a table where the unwanted rows outnumber the
wanted ones, use a window (see windows) or a narrower partition.