September 19, 2026 · 2 min read
Queries that say what they mean
An Entity declared once, screens that name the fields they read, and a query the server compiles from what it means: how Entity, Remote and Crud move this blog’s data.
Every screen of the studio reads data the same way. It says what it needs, and the Model fills in. No view fetches, and nothing keeps a cache the Model does not know about.
An Entity, declared once
A post is declared once with foldkit-entity, as the fields it has. Everything
else reads that one declaration: the server binds it to a table, the form edits it,
the CMS finds its slug in it, and a view asks for a selection of it:
export const PostPage = Entity.select(Post, {
id: true,
title: true,
slug: true,
excerpt: true,
cover: true,
body: true,
publishedAt: true,
})Remote: the server’s facts, in the Model
foldkit-remote keeps what the server owns (posts, pages, drafts) as a cache
inside the Model, each record once. A screen that is showing declares what it reads.
A Subscription turns that into requests for what is missing, the answers come back
as Messages, and one reducer puts them in the Model. When the screen goes,
its need goes with it, and the data can be let go.
Losing that cache is never losing work: the facts were the server’s, and asking again is how it recovers. Work that is not yet on the server, like a draft being typed, is the form’s, and the CMS saves it.
A query is a value
The blog’s index reads the newest published posts. The query is declared by what it means, not by an address to fetch:
export const RecentPosts = Query.define('RecentPosts', {}, () =>
Query.from(Post).pipe(
Query.where(Expr.isNotNull(Post.fields.publishedAt)),
Query.orderBy(Order.desc(Post.fields.publishedAt)),
),
)The server compiles it to SQL with foldkit-remote-drizzle, and the client asks
for it by name. There is no endpoint to write, and no second copy of the question
to keep in step. The server still decides what the asker may see: it answers
a visitor and an editor with only what is published.
Crud, in brief
The studio’s list of posts is foldkit-crud. A list joins a query to a selection:
export const WorklistList = Crud.list('Worklist', {
query: Cms.Entries,
selection: EntryRow,
})foldkit-mixins-crud draws it as a table whose rows open the entry. A Crud editor
joins a form to the mutation its value feeds: it loads what the form edits,
fills the form, turns a valid submit into the mutation, and says how it went.
The CMS’s editor has the same shape, with drafts and publishing added.
Nothing is generated. The application declares the form and the mutation, and Crud only joins them, so a screen does what those two say and nothing more. The post on Crud goes further.