Live-Daten aus der Datenbank.
Ganz ohne Verkabelung.

Das Schwierigste an einer Echtzeit-App ist, den Client mit der Datenbank synchron zu halten. Voltro macht genau das zum einfachen Teil: Query deklarieren, mit einem Hook subscriben — und jeder Schreibvorgang fließt per WebSocket zu jedem verbundenen Client. Kein Polling. Kein Refetch. Keine Cache-Invalidierung, die man vergessen könnte.

posts.list.query.ts
TypeScript
// queries/posts.list.query.ts
import { defineQuery } from '@voltro/protocol'
import { Schema } from 'effect'

export const listPosts = defineQuery({
  name:   'posts.list',
  source: 'posts',                 // ← drives auto-invalidation
  input:  Schema.Struct({}),
  output: Schema.Array(Post),
})

// queries/posts.list.query.server.ts
import { database } from '../database/schema'

export default (_input, _ctx) =>
  database.posts.orderBy('createdAt', 'desc').limit(100)

Reaktivität ist ein Runtime-Feature, kein Plugin.

Jede Voltro-App bekommt diese Primitive standardmäßig. Kein zusätzliches Paket, kein Opt-in-Flag — schalte `tenant()` an einer Tabelle ab, und sie pusht trotzdem; entferne die source:-Deklaration, und nur der manuelle Cache-Bust-Pfad entfällt.

Ein Hook, Live-Daten

useSubscription(api, tag) öffnet eine WebSocket-Subscription, gecacht nach (api, tag, input). Zwei Komponenten, die dieselben Daten anfragen, teilen sich eine Upstream-Verbindung.

Postgres LISTEN/NOTIFY nativ

Auf Postgres laufen Change-Events über das eingebaute Pub/Sub der Datenbank. Kein Redis nötig. Andere Dialekte nutzen Binlog-CDC (MariaDB) oder den @voltro/plugin-broadcast-Bus (MySQL, MSSQL).

Auto-optimistische Patches

Deklariere `target` an einer Mutation; der Client wendet den Patch vorab auf jede passende aktive Subscription an. Das Server-Delta ersetzt die optimistische Zeile, sobald es eintrifft. Fehler werden automatisch zurückgerollt.

Tenant-scoped by default

Trägt eine Tabelle den tenant()-Mixin, merged die Runtime das Tenant-Prädikat per AND in jede Subscription. Cross-Tenant-Änderungen wecken nie den falschen Subscriber.

Feld-genauer Relevanzfilter

Der Dispatcher verfolgt, von welchen Spalten jede Subscription abhängt (Projektion + Prädikat + Order + jeder Join-Key). Ein Write auf eine unbeteiligte Spalte überspringt die Re-Query komplett — kein SQL-Roundtrip, kein Delta.

Computed + eager-loaded

Gib einen geformten Wert mit Relationen zurück: useSubscription erhält DASSELBE Objekt, das der Server liefert — voll typisiert, mit eager-geladenen Kindern. Die Reaktivität berechnet die ganze Form bei jeder Änderung an der Quell-Tabellenmenge neu.

Die Client-Seite ist derselbe Hook, den du ohnehin schreiben würdest.

PostsList.tsx
TypeScript
// In any React component
import { useSubscription } from '@voltro/web'

const PostsList = () => {
  const { data: posts } = useSubscription<Post[]>(
    'app', 'posts.list',
  )
  // posts updates whenever ANY mutation writes to the
  // 'posts' table. No useEffect. No refetch.
  return (
    <ul>
      {posts?.map(p => <li key={p.id}>{p.body}</li>)}
    </ul>
  )
}

Das, was du sonst selbst hättest bauen müssen.

Ohne reaktive Primitive ist eine Echtzeit-Listenansicht eine Vier-System-Integration: eine Datenbank, ein Change-Feed (LISTEN/NOTIFY, CDC, Polling), ein Pub/Sub-Bus (Redis, NATS) und ein Client-Cache (TanStack, SWR). Jedes System hat eigene Retry-Semantik, eigene Auth-Geschichte, eigene Observability-Lücke, eigenes Deploy-Rollback. Voltro bindet sie alle an EINE Invariante — die source:-Deklaration an der Query — und schenkt dir reaktive Listen.

Die Regelschleife, in einem Satz:

Eine Mutation landet in Postgres → die Runtime fängt die Änderung per LISTEN/NOTIFY → der Matcher geht jede offene Subscription durch, deren Quell-Set die geänderte Tabelle enthält → der Relevanz-Vorfilter vergleicht die geänderten Spalten mit dem Abhängigkeits-Set jeder Subscription → passende Subscriptions re-queryen im Batch → das Diff (Snapshot oder Delta) geht per WebSocket an den Client → useSubscriptions data-Prop aktualisiert sich und React rendert neu.

Reaktivität komponiert mit dem Rest der Runtime.

Öffne das Framework. Schau es dir selbst an.

Jede Primitive auf dieser Seite ist heute im Framework. Klone den Starter, lass `voltro dev` laufen, in zwei Minuten ist es auf dem Bildschirm.