HN präsentiert: Triplit - Open-Source-Synchronisationsdatenbank, die auf Server und Client läuft
(github.com/aspen-cloud)- Triplit ist eine Open-Source-Datenbank, die Daten in Echtzeit zwischen Server und Browser synchronisiert, und versteht sich als Full-Stack-Datenbank, die als Typescript-Paket in Apps eingebunden wird
- Sie übernimmt sowohl die Serverspeicherung als auch die Synchronisierung von Client-Abfragen und unterstützt inkrementelle Updates, Konfliktauflösung auf Attributebene, lokales Caching, Offline-Modus und automatische Wiederverbindung
- Unterstützt pluggbare Speicher wie SQLite, IndexedDB, LevelDB und Memory und bietet serverseitige persistente Speicherung sowie ein Verwaltungs-Dashboard
- Die Query- und Änderungs-APIs können in React und vanilla Javascript verwendet werden; bereitgestellt wird sie als Monorepo mit React-/Svelte-Bindings, CLI, Console, Server und mehr
- Für schnelle Interaktionen bietet sie optimistische Updates, Rollback und Wiederholungen fehlgeschlagener Updates, serverseitig erzwungene Lese- und Schreibberechtigungen sowie kollaborative Funktionen auf Basis von CRDTs
Was Triplit bietet
- Triplit ist eine Open-Source-Datenbank, die Daten in Echtzeit synchronisiert zwischen Server und Browser
- Sie stellt einen synchronisierten Datenspeicher bereit, der als Typescript-Paket zu einer App hinzugefügt werden kann
- Sie speichert Daten auf dem Server und synchronisiert Client-Abfragen intelligent
- Diese Form bezeichnet Triplit als Full-Stack-Datenbank
- Es gibt auch ein Video eines Vortrags aus der Local-First-Community: presentation
Wichtige Funktionen
-
Echtzeit-Synchronisierung
- Unterstützt inkrementelle Updates
- Bietet Konfliktauflösung auf Attributebene
-
Local-First-Nutzbarkeit
- Bietet lokales Caching auf Basis einer vollständigen clientseitigen Datenbank
- Sorgt mit optimistischen Updates dafür, dass sich alle Interaktionen schnell anfühlen
- Unterstützt Offline-Modus, automatische Wiederverbindung und Konsistenzgarantien
-
Serverspeicherung und Betrieb
- Bietet serverseitige persistente Speicherung
- Enthält ein Verwaltungs-Dashboard
- Unterstützt pluggbare Speicheranbieter wie SQLite, IndexedDB, LevelDB und Memory
-
Datenmodell und API
- Unterstützt relationale Abfragen für komplexe Datenmodelle
- Bietet über Schemata Datensicherheit und Typescript-Autovervollständigung
- Stellt einfache APIs für Abfragen und Änderungen in vanilla Javascript und React bereit
-
Zusammenarbeit und Sicherheit
- Erzwingt serverseitig Berechtigungen sowohl für Lesen als auch Schreiben
- Bietet kollaborative und Multiplayer-Funktionen auf Basis von CRDTs
- Verwendet Delta-Patches, um den Netzwerkverkehr zu reduzieren und auf geringe Latenz auszurichten
- Verwaltet Rollback und Wiederholungen für fehlgeschlagene Updates
Aufbau des Monorepos
- TriplitDB: Eine für JS-Umgebungen wie Browser, Node, Deno und React Native konzipierte DB, die schnelle, Live-aktualisierte Abfragen bereitstellt und dabei Konsistenz bei mehreren Schreibenden über das Netzwerk hinweg aufrechterhält
- Client: Browser-Bibliothek zur Interaktion mit lokalem und entferntem TriplitDB
- CLI: Kommandozeilen-Tool für Projekt-Scaffolding, das Starten einer Full-Stack-Entwicklungsumgebung, Server-Migrationen und mehr
- React: React-Binding für
@triplit/client - Svelte: Svelte-Binding für
@triplit/client - Console: App zum Anzeigen und Ändern von Daten in Triplit-Projekten sowie zur Verwaltung von Schemata
- Server: Node-Server zur Synchronisierung von Daten zwischen Triplit-Clients
- Server-core: Protokollunabhängige Bibliothek zum Erstellen von Triplit-Servern
- Docs: Mit Nextra erstellte Triplit-Dokumentation
- Types: Von Triplit-Projekten gemeinsam genutzte Typen
- UI: Gemeinsame UI-Komponenten auf Basis von shadcn
Schneller Einstieg
- Ein neues Projekt startet mit
npm create triplit-app@latest my-app - In ein bestehendes Projekt wird zunächst
@triplit/clials Entwicklungsabhängigkeit installiert, danach wirdnpm run triplit initausgeführt - In
my-app/triplit/schema.tswird das Schema definiert- Das Beispiel definiert in der Collection
todosdie Felderid,textundcompleted completedist als Boolean-Feld mit dem Standardwertfalsegesetzt
- Das Beispiel definiert in der Collection
- Mit
npm run triplit devwird der Synchronisationsserver für die Entwicklung gestartet - Der Entwicklungsserver gibt die Umgebungsvariablen aus, die die App für die Synchronisierung mit dem Server benötigt
- Im Vite-Beispiel:
VITE_TRIPLIT_SERVER_URL=http://localhost:6543 VITE_TRIPLIT_TOKEN=copied-in-from-triplit-dev
- Im Vite-Beispiel:
React-Beispiel und Überprüfung der Synchronisierung
- Das React-Beispiel verwendet
TriplitClientunduseQuery - Der Client wird mit Schema, Server-URL und Token erstellt
- Mit
useQuery(client.query('todos'))wird das Abfrageergebnis fürtodosabonniert - Beim Ändern eines Kontrollkästchens wird mit
client.updateder Wert voncompletedumgeschaltet - Nach dem Start der App kann durch Öffnen eines weiteren Browser-Tabs bestätigt werden, dass die Daten in Echtzeit synchronisiert werden
Dokumentation und Kontaktkanäle
- Vollständige Einstiegsanleitung: getting started guide
- Ausführlicheres Tutorial: building a real-time todo app with Triplit, Vite, and React
- Fragen, Starthilfe und Vorschauen auf neue Funktionen gibt es auf Discord
- Neueste Ankündigungen gibt es auf Twitter/X
1 Kommentare
Meinungen auf Hacker News
Ich habe Triplit in einem Projekt https://github.com/thanhnguyen2187/cryptaa ausprobiert, und es funktionierte wie erwartet.
Das Datenmodell passt gut zu einer eher verteilten/P2P-nahen Idee, statt eine einzelne zentrale DB als Source of Truth zu haben, aber Self-Hosting und Abfragesprache lassen zu wünschen übrig.
In der Dokumentation war nicht klar beschrieben, wie man Server-Auth-Token erzeugt, daher habe ich mit dem CLI-Befehl
devein Token erstellt; dass das Token in einem Systemdienst im Klartext in Logs landet, ist aus Sicherheitssicht nicht gut, setzt aber wohl ein größeres Problem mit Zugriffsrechten voraus.Dem eigenen Query-DSL fehlt im Vergleich zu SQL Ausdrucksstärke wie
UNIQUEoderCOUNT, sodass ich einige Aggregationen selbst erledigen muss.Kürzlich habe ich mir Evolu https://www.evolu.dev/docs angesehen; Umfang und Funktionen wirken ähnlich. Triplit hat
.subscribe(), Evolu nicht; Evolu nutzt typisiertes SQL auf Basis von Kysely, wodurch Abfragen vertrauter und fortgeschrittener sind. Im Browser verwendet Evolu SQLite auf OPFS, während Triplit offenbar IndexedDB nutzt.Mein Beitrag auf Reddit: https://www.reddit.com/r/sveltejs/comments/1dndpj8/cryptaa_a...
Bei den Abfragen gibt es noch keine Aggregationen, sie stehen aber auf der Roadmap. Mit der inkrementellen Query-Engine könnten sich hier spannende Möglichkeiten ergeben.
Ein Daten-Dashboard, das sich etwa stündlich aktualisiert, müsste in bestehenden Systemen (Postgres, MongoDB usw.) jede Abfrage jedes Mal von Grund auf neu ausführen; wenn man in einer eher Materialize-ähnlichen Weise nur neue Daten verarbeitet, lässt es sich viel effizienter kontinuierlich aktualisieren.
Evolu habe ich selbst noch nicht ausprobiert, aber vielleicht gibt es auf Discord jemanden, der einen Vergleich gemacht hat: https://triplit.dev/discord
useQueryoder auf eine entkoppelte Weise.Bei DBs mit solch großartigen Offline-Sync-Protokollen frage ich mich, wie Schema-Evolution gehandhabt wird, wenn man unterschiedliche Client-Versionen nicht gleichzeitig upgraden kann.
Ich habe früher bei einer mobilen Health-App in genau diesem Kontext mit diesem Problem zu kämpfen gehabt.
Falls nötig, schreibt man per Dual Write gleichzeitig in beide Versionen.
Das ähnelt einer unterbrechungsfreien Live-Migration von Breaking Changes in einer SQL-DB, nur dass der Zeitpunkt des Umstiegs vom Kunden abhängt und man die Logik daher länger beibehalten muss.
Wichtig ist auch eine Tabelle, die die aktuelle Version koordiniert; bei Breaking Changes sollten zurückgebliebene Clients den Nutzer zum Upgrade auffordern.
In Verbindung mit der von allen Clients unterstützten Mindestversion kann man auch festlegen, wie lange Dual Reads/Writes beibehalten werden.
Triplit zeigt eine Warnung an, wenn man eine nicht abwärtskompatible Änderung erstellt; das ist in der Dokumentation zu sehen: https://www.triplit.dev/docs/schemas/updating#pushing-the-sc...
Mit der Zeit kann dadurch allerdings ganz natürlich eine unordentliche Schema-Definition mit vielen verwirrenden Namen entstehen.
Eine Lösung dafür haben wir noch nicht veröffentlicht, aber wir arbeiten an ein paar Dingen, die es weniger schmerzhaft machen sollen; als Hintergrund zu verschiedenen Ansätzen ist das Cambria-Paper hervorragend: https://www.inkandswitch.com/cambria/
Ein Nutzer könnte sein Handy zwei Jahre lang in der Schublade gelassen haben; jeder Client sollte sich dann einfach so schnell wie möglich selbst migrieren.
Dann müsste nicht jeder seine eigene Migrationsverwaltung neu erfinden.
Ich verstehe nicht so recht, für welche Apps es in Ordnung ist, dass Clients direkt in die DB schreiben können, und wie das ohne Backend-Logik tragfähig sein soll.
Bei Supabase und Firestore habe ich dieselbe Frage, also übersehe ich vermutlich etwas.
In Unternehmensumgebungen ist es natürlich umgekehrt, und Diskussionen, die das ignorieren, sind frustrierend.
Besonders wenn man auf Tech Twitter Beiträge sieht, die einen bestimmten Stack oder eine bestimmte Arbeitsweise verteidigen, ist oft sehr offensichtlich, dass die Leute nie Business-Systeme gebaut haben, sondern nur CRUD; dann verstehen sie nicht, warum erfahrene Entwickler nicht zustimmen.
Für Dinge mit viel Backend-Logik scheint es mir nicht gut geeignet zu sein.
In Supabase gibt es zum Beispiel eine Funktion namens Row Level Security.
Der Client kann zwar Requests an Supabase senden, aber Supabase führt im Backend zusätzliche Abfragen aus, um zu entscheiden, ob der eingehende Request erlaubt ist.
Als einfaches Beispiel kann man festlegen, dass eine Zeile nur gelesen, geschrieben und aktualisiert werden darf, wenn der Wert der Spalte
UserIDdem authentifizierten Nutzer entspricht, der den Request gestellt hat.Wir haben Benutzereinstellungen in Triplit gespeichert, und diese Einstellungen mussten von Administratoren verwaltet werden können.
Die Nutzer sollten das Gefühl haben, dass die App immer lokal läuft, und die Internetqualität ist häufig nicht besonders gut. Gleichzeitig wird die App aber auf mehreren Geräten genutzt, und Administratoren müssen die Einstellungen anderer Nutzer einsehen und verwalten können – daher war Synchronisierung nötig.
Insgesamt waren sowohl die Frontend-Developer-Experience als auch der Support bei Triplit hervorragend. Wenn wir ein Issue oder einen Feature Request gefunden haben, hat das Team sehr schnell reagiert.
Sobald es eine Antwort auf High-Availability-Deployments gibt, planen wir, auch wichtigere Daten von Postgres dorthin zu verschieben.
Ich frage mich, warum die AGPL-Lizenz gewählt wurde.
Ich glaube, ich habe die YouTube-Präsentation https://www.youtube.com/playlist?list=PLTbD2QA-VMnXFsLbuPGz1... auf dem Local First Discord Server https://localfirstweb.dev/ gesehen, daher freue ich mich, das bei Show HN zu sehen.
Da ich kein TypeScript verwende, gehöre ich vielleicht nicht zur primären Zielgruppe. Ich nutze Local First vor allem in Mobile-Apps mit unzuverlässiger Verbindung, anders als im Web, und verwende Flutter sowie ein Rust-Backend.
Andere Local-First-Lösungen wie ElectricSQL und PowerSync synchronisieren Client- und Server-DB direkt und sind dadurch unabhängiger von Client/Server.
CRDT-basierte Lösungen lassen sich ebenfalls per FFI auf Client und Server nutzen. Zum Beispiel ist automerge in Rust geschrieben, sodass man es auf Flutter-Seite per FFI über
flutter_rust_bridge, im Web per WASM und im Backend mit Rust verwenden kann.Triplit wirkt eher wie eine klassischere Client-Server-Synchronisierung, bei der der Server die Source of Truth ist, statt konfliktfreie Auflösung zwischen unterschiedlichen Clients in den Vordergrund zu stellen.
Ich frage mich, warum ihr euch für eine Lösung auf Sprachebene entschieden habt statt für einen stärker client- und serverunabhängigen Ansatz auf DB-Schicht. Es wirkt so, als dürfte es künftig schwierig werden, andere Sprachen und Frameworks außerhalb des JS-Ökosystems zu unterstützen.
Außerdem scheint ihr mit Supabase konkurrieren zu wollen; Supabase experimentiert aber ebenfalls mit Synchronisierung auf DB-Ebene in Postgres und mit CRDTs, sodass sie möglicherweise aufholen könnten: https://news.ycombinator.com/item?id=33931971
Für den Anfang haben wir uns jedoch entschieden, uns auf reines TypeScript zu konzentrieren. Wir glauben, dass der Markt groß genug ist, wir an die Zukunft von PWAs glauben und dass wir nur durch diesen Fokus die beste Experience schaffen können.
Irgendwann werden wir vermutlich etwas Plattformunabhängigeres bauen, aber der Zeitpunkt ist noch unklar.
Die Teams von ElectricSQL und Supabase sind beide hervorragend und durchdacht und werden im SQL-Bereich wohl weiter wachsen; genau das ist der grundlegendste Unterschied im Ansatz.
Triplit geht davon aus, dass man Entwicklern die beste Experience bieten kann, indem man SQL vermeidet, und es gibt genügend Raum, damit beide Philosophien koexistieren können.
Bei LWW frage ich mich, ob die Informationsmenge auf dem Client linear mit der Anzahl der Operationen wächst.
Anders gesagt: Wächst das Operation Log immer weiter, je mehr ein Nutzer die DB verändert, oder gibt es Checkpoints? Wie skaliert das beim Speicherplatz, wenn ein Nutzer pro Tag Millionen von Operationen ausführt?
Ein LWW-Register selbst erfordert allerdings nicht, dass Historie gespeichert wird; das ist lediglich die aktuelle Implementierung, um auch Clients effizient synchronisieren zu können, die lange offline waren.
Wir können noch nicht wirklich behaupten, bereits bei einer Million Operationen pro Tag angekommen zu sein, aber es hat Vorteile, dass der Server autoritativ ist.
Künftig könnte der Triplit-Server den letzten Synchronisierungs-Timestamp jedes Clients verfolgen und die Historie schrittweise beschneiden, ähnlich wie Postgres tote Tupel per
VACUUMbereinigt.Rust-Bindings, damit man es in Tauri verwenden kann, wären schön.
Zusammen mit dem Wachstum von Tauri, der bald kommenden Unterstützung für mobile Geräte und der jüngsten Popularität von SQLite könnte das die Lücke für Offline-First-Apps schließen und für viele Entwicklerteams zur Standardwahl werden.
ElectricSQL arbeitet auf DB-Schicht und ist dadurch sprachunabhängig; außerdem wird serverseitig Rust verwendet, sodass Rust-Bindings sowohl auf Client- als auch auf Serverseite funktionieren könnten.
Wenn du mitentwickeln möchtest, sag gern Bescheid.
Wenn das stimmt, sollte Triplit direkt funktionieren.
Ich nutze Triplit seit einiger Zeit in einer React-Native-App, und es funktioniert sehr gut.
Klare Empfehlung; es war die einzige Local-First-DB, die alle meine Anforderungen erfüllt hat.
Eine passende und vernünftige Query Language (nicht SQL), hervorragende TypeScript-Unterstützung, Offline-Support, React-Native-Unterstützung – und dass es Open Source ist und selbst gehostet werden kann, ist ebenfalls gut.
Ich frage mich, ob man es nicht zusammen mit einer bestehenden PostgreSQL-DB verwenden kann.
Es ist noch nicht ganz bereit für eine Veröffentlichung, aber wir wollen es bald für andere testbar machen.
Ich selbst tendiere ebenfalls dazu, das zu verwenden.