Skip to content
Onboarding contents

OnboardingStart here

Welcome to Rindle

Maintain query results as data changes. Use a local engine, embed a database, stream server results, or build an optimistic synced app.

View as Markdown

Rindle keeps query results up to date as your data changes. You define a query once, and the engine maintains a live view of its result.

An issue list is one example. New issues appear, closed issues leave, and changes to priority update the order. Your query describes the result. Rindle computes the changes that keep that result correct.

You choose how much of Rindle your application uses. A local store can maintain views over rows you supply. An embedded database can capture SQL writes. A browser can receive server results, keep local queryable rows, or also predict writes.

These choices share query concepts, but they have different storage, transport, and write APIs. Start with the common idea here. Then choose your first project.

Why Rindle?

  • Keep views current. Rindle maintains query results after writes. You do not need a separate refresh schedule or invalidation rule for each view.
  • Describe the data you need. Typed queries express filters, ordering, limits, and relationships. Your application subscribes to the resulting rows.
  • Make local interactions responsive. In a synced app, the browser runs the engine over local data. Optimistic writes update affected views before the server responds.

The technique behind live views is incremental view maintenance (IVM). The engine uses each write to update affected results. The work depends on the query, indexes, and affected rows. A change can also affect related rows.

The correctness rule is the same everywhere: view-after-write == fresh-query. After the engine applies a write, the maintained result equals a fresh query over the same data.

Rows in, view out

This example runs the engine in memory through @rindle/wasm. It needs a browser project with the package installed. The browser guide covers installation and initialization.

import {
  table, string, number, boolean, createSchema, createWasmStore,
} from "@rindle/wasm";

const issue = table("issue")
  .columns({ id: number(), title: string(), closed: boolean() })
  .primaryKey("id");

const store = await createWasmStore(createSchema({ tables: [issue] }));
const view = store.query.issue
  .where.closed(false)
  .orderBy("id", "asc")
  .materialize();

const unsubscribe = view.subscribe((rows) => console.log(rows));
// First result: []

const first = { id: 1, title: "Try a live query", closed: false };
await store.write((tx) => tx.add("issue", first));
// Result: [{ id: 1, title: "Try a live query", closed: false }]

await store.write((tx) => tx.edit("issue", first, { ...first, closed: true }));
// Result: []

unsubscribe();
view.destroy();

materialize() creates the live view. subscribe() delivers its current rows, then the updated rows after each write that changes the result. The final two calls release the subscription and view.

This standalone store has no persistence or sync. Its TypeScript schema defines the local tables. For a database-backed app, SQL defines the tables, and Rindle generates the TypeScript schema from them.

How the pieces fit

The engine maintains views over rows. The surrounding packages determine where those rows live, how writes reach them, and how results reach your application.

Capability What it adds Start here
Local live views Queries over rows your application supplies Standalone WASM or raw Rust engine
Embedded SQLite database Ordinary SQL writes, automatic change capture, and live query events inside your process Rust rindle-replica, Node addon
Server result streams Query results delivered to a browser without a local WASM engine Remote client and protocol requirements
Local queries over server data Normalized row subscriptions feeding a browser engine, with optional optimistic writes Browser client choices
Rindle SQL SQL requests and transactions over HTTP, with no browser client required SQL client
Server read models Live results retained between requests, including queries with no subscribers Pinned queries
Complete optimistic sync Query leases, local prediction, mutation delivery, and reconciliation with authoritative data createRindleClient, app scaffold
UI integrations Component subscriptions, route preloads, and server rendering Fragments, TanStack Start, SSR

These capabilities can compose. A service can write SQL while a browser subscribes to a live query over the same tables. A server can read a pinned result without a browser. The client chooser explains which transports work together; a shared query API does not make their wire protocols interchangeable.

PostgreSQL can remain the source of truth through a separate preview integration. Rindle SQL and this integration have different setup requirements and limitations.

SQL, queries, and sync

SQL defines database tables and performs database reads and writes. A live query uses the supported query builder to describe a maintained result. An ordinary SQL SELECT returns rows for that request. It does not create a subscription.

In a synced app, a named query identifies the data a client requests. Your API server resolves that name and its arguments under the authenticated user. A mutator describes a write. The browser predicts its effect locally, and the server applies it to authoritative data.

The browser holds the data delivered by its subscriptions. It can query those local rows while disconnected, but cannot fetch missing rows until it reconnects. Optimistic results can change after server confirmation or rejection.

Framework adapters build on this model. TanStack Start and SSR are optional integrations. A standalone browser store needs neither a server nor a framework.

Packages and licensing

The Rust manifests for the engine, SQLite backend, replica runtime, daemon, and replicator declare Apache-2.0. These crates are not published to crates.io. The crate map lists source dependencies, bindings, and package availability. Hosted service plans are separate from the library APIs.

Learn enough to build

  1. Decide whether it fits. Read Is Rindle for you? for the tradeoffs and supported workloads.
  2. Run one example. Getting started lists the setup and reading sequence for each kind of application.
  3. Build on your example. Guides cover queries, writes, UI integration, and deployment.
  4. Look up an API. Reference maps packages, query shapes, and configuration options to their documentation.

The header separates Onboarding, Guides, and Reference. Each section has its own sidebar. The optional integration filter narrows that section to Engine & SQL or Synced apps.

For a coding assistant, start with Rindle for coding agents. Every page is also available as Markdown from the same source.