HN compartido: Triplit - base de datos de sincronización open source que corre en servidor y cliente
(github.com/aspen-cloud)- Triplit es una base de datos open source que sincroniza datos en tiempo real entre el servidor y el navegador, y se presenta como una base de datos full-stack que se usa integrándola en la app como paquete de Typescript
- Maneja tanto el almacenamiento en servidor como la sincronización de consultas del cliente, y soporta actualizaciones incrementales, resolución de conflictos a nivel de propiedad, caché local, modo offline y reconexión automática
- Soporta almacenamientos conectables como SQLite, IndexedDB, LevelDB y Memory, y ofrece almacenamiento persistente del lado del servidor junto con un panel de administración
- Se pueden usar APIs de consultas y cambios en React y vanilla Javascript, y se ofrece como un monorepo compuesto por bindings para React y Svelte, CLI, Console, Server y más
- También ofrece actualizaciones optimistas para interacciones rápidas, rollback y reintentos de actualizaciones fallidas, permisos de lectura y escritura forzados desde el servidor, y funciones colaborativas basadas en CRDT
Qué ofrece Triplit
- Triplit es una base de datos open source que sincroniza datos en tiempo real entre el servidor y el navegador
- Ofrece un almacén de datos sincronizado que puede agregarse a una app como paquete de Typescript
- Guarda los datos en el servidor y sincroniza inteligentemente las consultas del cliente
- A este enfoque, Triplit lo llama base de datos full-stack
- También se ofrece un video de presentación de la comunidad Local First: presentation
Funciones principales
-
Sincronización en tiempo real
- Soporta actualizaciones incrementales
- Ofrece resolución de conflictos a nivel de propiedad
-
Usabilidad local-first
- Ofrece caché local basada en una base de datos completa del lado del cliente
- Hace que todas las interacciones se sientan rápidas con actualizaciones optimistas
- Soporta modo offline, reconexión automática y garantía de consistencia
-
Almacenamiento y operación en servidor
- Ofrece almacenamiento persistente del lado del servidor
- Incluye un panel de administración
- Soporta proveedores de almacenamiento conectables como SQLite, IndexedDB, LevelDB y Memory
-
Modelo de datos y API
- Soporta consultas relacionales para modelos de datos complejos
- Proporciona seguridad de datos y autocompletado de Typescript mediante esquemas
- Ofrece una API simple para consultas y cambios en vanilla Javascript y React
-
Colaboración y seguridad
- Fuerza permisos tanto de lectura como de escritura desde el servidor
- Ofrece funciones de colaboración y multijugador basadas en CRDTs
- Usa parches delta para reducir el tráfico de red y apuntar a baja latencia
- Gestiona rollback y reintentos para actualizaciones fallidas
Estructura del monorepo
- TriplitDB: DB diseñada para ejecutarse en entornos JS como navegador, Node, Deno y React Native, que ofrece consultas rápidas y con actualizaciones en vivo manteniendo consistencia con múltiples escritores en red
- Client: biblioteca de navegador para interactuar con TriplitDB local y remoto
- CLI: herramienta de línea de comandos para scaffolding de proyectos, ejecución del entorno de desarrollo full-stack, migraciones de servidor y más
- React: binding de React para
@triplit/client - Svelte: binding de Svelte para
@triplit/client - Console: app para ver y modificar datos de proyectos Triplit y administrar esquemas
- Server: servidor Node que sincroniza datos entre clientes de Triplit
- Server-core: biblioteca independiente del protocolo para construir servidores Triplit
- Docs: documentación de Triplit hecha con Nextra
- Types: tipos compartidos entre proyectos Triplit
- UI: componentes UI compartidos basados en shadcn
Flujo de inicio rápido
- Un proyecto nuevo comienza con
npm create triplit-app@latest my-app - En un proyecto existente, instala
@triplit/clicomo dependencia de desarrollo y luego ejecutanpm run triplit init - Define el esquema en
my-app/triplit/schema.ts- El ejemplo define los campos
id,textycompleteden la coleccióntodos completedse configura como un campo Boolean con valor por defectofalse
- El ejemplo define los campos
- Inicia el servidor de sincronización para desarrollo con
npm run triplit dev - El servidor de desarrollo imprime las variables de entorno necesarias para que la app se sincronice con el servidor
- En el ejemplo con Vite:
VITE_TRIPLIT_SERVER_URL=http://localhost:6543 VITE_TRIPLIT_TOKEN=copied-in-from-triplit-dev
- En el ejemplo con Vite:
Ejemplo de uso con React y verificación de sincronización
- El ejemplo en React usa
TriplitClientyuseQuery - El cliente se crea recibiendo el esquema, la URL del servidor y el token
- Se suscribe al resultado de la consulta
todosconuseQuery(client.query('todos')) - Al cambiar el checkbox, invierte el valor de
completedconclient.update - Después de iniciar la app, al abrir otra pestaña del navegador se puede confirmar que los datos se sincronizan en tiempo real
Documentación y canales de contacto
- Guía completa de inicio: getting started guide
- Tutorial más detallado: building a real-time todo app with Triplit, Vite, and React
- Preguntas, ayuda para comenzar y adelantos de nuevas funciones están disponibles en Discord
- Los anuncios más recientes pueden verse en Twitter/X
1 comentarios
Opiniones de Hacker News
Probé Triplit en el proyecto https://github.com/thanhnguyen2187/cryptaa y funcionó como esperaba.
El modelo de datos encaja bien con una idea más distribuida/P2P que con una única base de datos central como fuente de verdad, pero el self-hosting y el lenguaje de consultas dejan que desear.
La documentación no dejaba claro cómo generar tokens de autenticación del servidor, así que creé uno con el comando
devde la CLI; que el token quede en logs en texto plano en un servicio del sistema no es bueno para la seguridad, aunque creo que presupone un problema mayor de permisos de acceso.El DSL de consultas personalizado no tiene la expresividad de SQL, como
UNIQUEoCOUNT, así que tuve que hacer algunas agregaciones manualmente.Hace poco vi Evolu https://www.evolu.dev/docs y parece tener un alcance y funciones similares; Triplit tiene
.subscribe()y Evolu no, Evolu usa SQL tipado basado en Kysely, por lo que las consultas resultan más familiares y avanzadas, y en el navegador Evolu usa SQLite sobre OPFS, mientras que Triplit parece usar IndexedDB.Publicación que hice en Reddit: https://www.reddit.com/r/sveltejs/comments/1dndpj8/cryptaa_a...
En cuanto a consultas, todavía no hay agregaciones, pero están en el roadmap; creemos que aprovechar el motor de consultas incremental abre posibilidades muy interesantes.
Por ejemplo, en un dashboard de datos que se actualiza cada hora, con sistemas tradicionales (Postgres, MongoDB, etc.) habría que volver a ejecutar la consulta desde cero cada vez, pero si se procesan solo los datos nuevos, con un enfoque más cercano a Materialize, se puede mantener actualizado de forma mucho más eficiente.
Todavía no probé Evolu directamente, pero puede que alguien lo haya comparado en Discord: https://triplit.dev/discord
useQueryo de forma separada.Cuando se usan DB con estos excelentes protocolos de sincronización offline, me pregunto cómo manejan la evolución del esquema cuando no se pueden actualizar al mismo tiempo distintas versiones de clientes.
Tengo el contexto de haber sufrido este problema antes en una app móvil de salud.
Si hace falta, terminas haciendo escrituras dobles en ambas versiones.
Es parecido a hacer una migración live sin downtime de un cambio incompatible en una DB SQL, pero como el momento de transición depende del cliente, tienes que mantener esa lógica durante más tiempo.
También es importante tener una tabla que coordine la versión más reciente, y si hay un cambio incompatible, hacer que los clientes atrasados le pidan al usuario que actualice.
En función de la versión mínima soportada en todos los clientes, también puedes decidir cuánto tiempo mantener las lecturas/escrituras dobles.
Triplit muestra una advertencia si generas un cambio que no es compatible hacia atrás, como se ve en la documentación: https://www.triplit.dev/docs/schemas/updating#pushing-the-sc...
Dicho eso, con el tiempo puede surgir naturalmente una definición de esquema desordenada, con muchos nombres confusos.
Todavía no lanzamos una solución para corregirlo, pero estamos trabajando en varias cosas para hacerlo menos doloroso; para el contexto de distintos enfoques, el documento de Cambria es excelente: https://www.inkandswitch.com/cambria/
Un usuario podría haber dejado el teléfono guardado en un cajón durante 2 años, así que basta con que cada cliente se migre a sí mismo lo antes posible.
Evitaría que todos tengan que reinventar su propia gestión de migraciones.
No termino de entender para qué apps está bien que el cliente pueda escribir directamente en la DB, ni cómo se sostiene eso sin lógica de backend.
Tengo la misma duda sobre Supabase y Firestore, así que siento que algo se me escapa.
En entornos empresariales obviamente ocurre lo contrario, y me frustra ver discusiones que ignoran eso.
Sobre todo en Twitter tecnológico, cuando veo a gente defender cierto stack o forma de trabajar, muchas veces es demasiado evidente que solo han hecho CRUD y nunca sistemas de negocio, por lo que no entienden por qué desarrolladores con experiencia no están de acuerdo.
No creo que encaje bien con cosas que tienen mucha lógica de backend.
En Supabase, por ejemplo, existe una función llamada seguridad a nivel de fila.
El cliente puede enviar solicitudes a Supabase, pero Supabase ejecuta consultas adicionales en el backend para decidir si la solicitud entrante está permitida.
Como ejemplo simple, se puede permitir leer, escribir y actualizar una fila solo cuando el valor de la columna
UserIDcoincide con el usuario autenticado que hizo la solicitud.Veníamos guardando las configuraciones de usuario en Triplit, y esas configuraciones debían poder ser administradas por un administrador
Los usuarios debían sentir que la app siempre corría localmente, y la calidad de internet a menudo no era buena, pero como usaban varios dispositivos y los administradores debían ver y gestionar la configuración de otros usuarios, hacía falta sincronización
En general, Triplit fue excelente tanto en la experiencia de desarrollo frontend como en el soporte; cuando encontrábamos issues o solicitudes de funciones, el equipo las resolvía muy rápido
Cuando haya una respuesta sobre el despliegue de alta disponibilidad, también planeamos mover datos más importantes en lugar de Postgres
Me da curiosidad por qué eligieron la licencia AGPL
Creo haber visto una presentación de YouTube https://www.youtube.com/playlist?list=PLTbD2QA-VMnXFsLbuPGz1... en el servidor de Discord de Local First https://localfirstweb.dev/, así que me da gusto verlo en Show HN
Como no uso TypeScript, quizá no sea el público objetivo principal, y a diferencia de la web, uso local-first sobre todo en apps móviles con conectividad inestable, con Flutter y un backend en Rust
Otras soluciones local-first como ElectricSQL y PowerSync sincronizan directamente las DB del cliente y del servidor, por lo que son más independientes del cliente/servidor
Las soluciones basadas en CRDT también pueden usarse en cliente y servidor mediante FFI; por ejemplo, automerge está en Rust, así que del lado de Flutter puede usarse con FFI vía
flutter_rust_bridge, en la web con WASM y en el backend con RustTriplit parece más una sincronización cliente-servidor clásica, donde el servidor es la fuente de verdad, que una resolución sin conflictos entre distintos clientes
Me da curiosidad por qué eligieron una solución a nivel de lenguaje en lugar de un enfoque de capa de DB más independiente del cliente y del servidor, y parece difícil que en el futuro soporten lenguajes y frameworks que no estén basados en JS
Además, parece que quieren competir con Supabase, pero Supabase también está experimentando con sincronización a nivel de DB en Postgres y CRDT, así que quizá podría alcanzarlos https://news.ycombinator.com/item?id=33931971
Sin embargo, decidimos empezar concentrándonos en TypeScript puro: creemos que el mercado es lo bastante grande, confiamos en el futuro de las PWA y pensamos que enfocarnos ahí nos permite crear la mejor experiencia
Algún día probablemente hagamos algo más independiente de la plataforma, pero no está claro cuándo
Los equipos de ElectricSQL y Supabase son excelentes y muy reflexivos, y parece que seguirán creciendo en el ámbito de SQL; esa es la diferencia más fundamental de enfoque
En Triplit creemos que evitar SQL permite ofrecer a los desarrolladores la mejor experiencia, y hay espacio suficiente para que ambas filosofías coexistan
Si es LWW, me pregunto si la cantidad de información del cliente crece linealmente con el número de operaciones
Es decir, si mientras el usuario modifica más la DB el log de operaciones sigue creciendo, o si usan checkpoints, y cómo escala en términos de espacio cuando un usuario hace millones de operaciones por día
Dicho eso, el registro LWW en sí no exige guardar historial; es solo la implementación actual para que incluso los clientes que estuvieron offline mucho tiempo puedan sincronizarse de forma eficiente
No diría que ya hayamos llegado del todo a un millón de operaciones al día, pero que el servidor tenga autoridad es una ventaja
En el futuro, el servidor de Triplit podría rastrear la marca de tiempo de la última sincronización de cada cliente y podar el historial progresivamente, de forma similar a como Postgres procesa con
VACUUMlas tuplas muertasSería bueno tener bindings de Rust para poder usarlo en Tauri
Si se combinan el crecimiento de Tauri, el soporte para dispositivos móviles que viene pronto y la reciente popularidad de SQLite, podría cerrar la brecha para apps offline-first y convertirse en la opción predeterminada para muchos equipos de desarrollo
ElectricSQL funciona en la capa de DB, así que es independiente del lenguaje, y como usa Rust en el servidor, los bindings de Rust podrían funcionar tanto en cliente como en servidor
Si quieres desarrollarlo en conjunto, avísame
Si es así, Triplit debería funcionar directamente
He estado usando Triplit durante un tiempo en una app de React Native y funciona muy bien
Lo recomiendo mucho; fue la única DB local-first que cumplía todos los requisitos que necesitaba
Tiene un lenguaje de consultas adecuado y sensato (no SQL), excelente soporte de TypeScript, soporte offline, soporte para React Native, y también me gusta que sea open source y self-hosteable
Me pregunto si no es posible usarlo junto con una DB PostgreSQL existente
Todavía no está lista para publicarse, pero queremos que la gente pueda probarla pronto
Yo también me estoy inclinando por usar eso