A pinned query is a live query whose result stays materialized with no subscribers. Use one for a frequently read public page, leaderboard, or service response. The engine updates the result as it applies source changes.
The consumer makes a one-shot request for rows. It does not need WebAssembly, browser sync, optimistic writes, or a UI framework.
Before you start
You need a Rindle deployment with live-query support and an API server connection. Define the query in the server query registry. Use the TypeScript query guide for supported shapes.
This example assumes the SQL-generated issue table from the
manual synced-app setup, with id, title, and
createdAt columns. It defines one public, fixed-size query and reads its rows
from server code:
import { defineQuery, newQueryBuilder } from "@rindle/client";
import { createRindleApiServer, registerQueries } from "@rindle/api-server";
import { schema } from "../shared/schema.gen.ts";
const q = newQueryBuilder(schema);
const publicIssues = defineQuery("publicIssues", () =>
q.issue.select("id", "title").orderBy("createdAt", "desc").limit(50),
);
const api = createRindleApiServer({
rindle: {}, // Reads RINDLE_URL and RINDLE_DATABASE_TOKEN on the server.
queries: registerQueries([publicIssues]),
pinnedQueries: [{ name: "publicIssues" }],
});
try {
await api.assertPins();
const result = await api.readQuery({
user: undefined,
name: "publicIssues",
args: null,
});
console.log(result.rows);
} finally {
api.close();
}
The sample query is intentionally public. It has no per-user filter. For a private query, supply your authenticated request context and authorize it as shown in the API server guide.
assertPins() creates the configured materializations. Repeating the call reuses
an existing result for the same canonical query. In a long-running service,
create the API instance at startup, assert its pins, and close it at shutdown.
Closing the API client’s transport does not unpin the daemon’s result.
Read the result
Your request handler can call api.readQuery or expose api.handleReadJson.
Both resolve the named query under the request context and apply the configured
query authorization. See the HTTP handler example
for request wiring.
A one-shot response contains { rows, cvMin, queryKey }. It returns the current
assembled rows without opening a subscription. The SSR integration
uses the same read operation to seed a page before browser hydration.
Reading the matching pinned query reuses its maintained result. An unpinned
one-shot query can also stay warm temporarily, controlled by readIdleTtlMs.
Choose what to pin
Pins resolve under pinUser, which defaults to undefined. Choose queries
that are independent of the current viewer, such as a public topic list.
Do not use a shared pin as a substitute for per-request authorization.
A request reuses the pin only with the same canonical query and visibility scope.
Pins have no per-viewer visibility key. A read scoped by subject or routingKey
can create a separate materialization, even with the same query arguments.
Different arguments or authorization filters can also produce a separate result.
The current lease path also assigns a pinned policy to any query whose name
appears in pinnedQueries. That includes other argument combinations leased under
that name, not only the arguments asserted at startup. Prefer a dedicated named
query with fixed or tightly bounded arguments. Otherwise, user-selected values
can leave many distinct results pinned.
Pins consume memory and maintenance work while no one reads them. Initial materialization reads the starting data. Later work depends on the query, indexes, and affected rows. Each response also serializes the result.
Keep pins available
The daemon does not persist materialization state across restarts. Call
assertPins() at startup and after a follower boot identifier changes.
Pinned means retained with no subscribers, not durable across a restart.
For a fleet, configure pinFanout to assert pins on all live followers.
Without it, assertPins() uses its configured daemon connection. The
API server guide describes
the lifecycle, and the deployment guide covers routing.
A pinned result is current for the source changes that its engine has applied. Replication lag can still separate a follower from the write authority. Use the deployment’s consistency guarantees when interpreting a read.