HN revela: Triplit - banco de dados de sincronização open source que roda no servidor e no cliente
(github.com/aspen-cloud)- Triplit é um banco de dados open source que sincroniza dados em tempo real entre o servidor e o navegador, e se apresenta como um banco de dados full-stack usado no app como um pacote Typescript
- Ele lida tanto com o armazenamento no servidor quanto com a sincronização de consultas no cliente, oferecendo atualizações incrementais, resolução de conflitos por atributo, cache local, modo offline e reconexão automática
- Suporta armazenamentos plugáveis como SQLite, IndexedDB, LevelDB e Memory, além de fornecer armazenamento persistente no lado do servidor e um dashboard de administração
- APIs de consulta e mutação podem ser usadas em React e Javascript vanilla, e ele é oferecido como um monorepo composto por React·Svelte bindings, CLI, Console e Server
- Também oferece atualizações otimistas para interações rápidas, rollback e nova tentativa de atualizações com falha, permissões de leitura e escrita impostas pelo servidor e recursos de colaboração baseados em CRDT
O que o Triplit oferece
- Triplit é um banco de dados open source que sincroniza dados em tempo real entre o servidor e o navegador
- Fornece um armazenamento de dados sincronizado que pode ser adicionado ao app como um pacote Typescript
- Armazena dados no servidor e sincroniza de forma inteligente as consultas do cliente
- O Triplit chama esse modelo de banco de dados full-stack
- Também há um vídeo de apresentação da comunidade Local First: presentation
Principais recursos
-
Sincronização em tempo real
- Suporta atualizações incrementais
- Oferece resolução de conflitos por atributo
-
Usabilidade local-first
- Fornece cache local com base em um banco de dados totalmente no lado do cliente
- Faz com que todas as interações pareçam rápidas com atualizações otimistas
- Suporta modo offline, reconexão automática e garantia de consistência
-
Armazenamento e operação no servidor
- Fornece armazenamento persistente no lado do servidor
- Inclui um dashboard de administração
- Suporta provedores de armazenamento plugáveis como SQLite, IndexedDB, LevelDB e Memory
-
Modelo de dados e API
- Suporta consultas relacionais para modelos de dados complexos
- Fornece segurança de dados e autocompletar do Typescript por meio de schema
- Oferece uma API simples para consultas e mutações em Javascript vanilla e React
-
Colaboração e segurança
- Impõe permissões no servidor tanto para leitura quanto para escrita
- Fornece recursos de colaboração e multiplayer baseados em CRDTs
- Usa delta patches para reduzir o tráfego de rede e buscar baixa latência
- Gerencia rollback e nova tentativa para atualizações com falha
Estrutura do monorepo
- TriplitDB: banco de dados projetado para rodar em ambientes JS como navegador, Node, Deno e React Native, oferecendo consultas rápidas com atualização ao vivo enquanto mantém consistência entre múltiplos autores na rede
- Client: biblioteca para navegador que interage com TriplitDB local e remoto
- CLI: ferramenta de linha de comando para scaffolding de projetos, execução do ambiente de desenvolvimento full-stack, migrações de servidor etc.
- React: React binding para
@triplit/client - Svelte: Svelte binding para
@triplit/client - Console: app para visualizar e alterar dados de projetos Triplit e gerenciar schema
- Server: servidor Node que sincroniza dados entre clientes Triplit
- Server-core: biblioteca independente de protocolo para criar servidores Triplit
- Docs: documentação do Triplit feita com Nextra
- Types: tipos compartilhados pelos projetos Triplit
- UI: componentes de UI compartilhados baseados em shadcn
Fluxo de início rápido
- Um novo projeto começa com
npm create triplit-app@latest my-app - Em um projeto existente, instale
@triplit/clicomo dependência de desenvolvimento e depois executenpm run triplit init - Defina o schema em
my-app/triplit/schema.ts- O exemplo define os campos
id,textecompletedna coleçãotodos completedé configurado como um campo Boolean com valor padrãofalse
- O exemplo define os campos
- Inicie o servidor de sincronização de desenvolvimento com
npm run triplit dev - O servidor de desenvolvimento exibe as variáveis de ambiente necessárias para o app sincronizar com o servidor
- No exemplo com Vite:
VITE_TRIPLIT_SERVER_URL=http://localhost:6543 VITE_TRIPLIT_TOKEN=copied-in-from-triplit-dev
- No exemplo com Vite:
Exemplo de uso com React e verificação da sincronização
- O exemplo com React usa
TriplitClienteuseQuery - O cliente é criado com schema, URL do servidor e token
- Use
useQuery(client.query('todos'))para assinar os resultados da consultatodos - Ao mudar o checkbox, use
client.updatepara alternar o valor decompleted - Depois de iniciar o app, ao abrir outra aba do navegador, é possível verificar que os dados são sincronizados em tempo real
Documentação e canais de contato
- Guia completo de início: getting started guide
- Tutorial mais detalhado: building a real-time todo app with Triplit, Vite, and React
- Perguntas, ajuda para começar e prévias de novos recursos estão disponíveis no Discord
- Os anúncios mais recentes podem ser vistos no Twitter/X
1 comentários
Opiniões no Hacker News
Usei o Triplit no projeto https://github.com/thanhnguyen2187/cryptaa e ele funcionou como esperado
O modelo de dados combina bem com uma concepção mais distribuída/P2P, em vez de ter um único banco de dados central como fonte da verdade, mas self-hosting e a linguagem de consulta deixam a desejar
Como a documentação não deixava claro como gerar tokens de autenticação do servidor, criei um token com o comando
devda CLI; o fato de o token ficar em logs em texto puro no serviço do sistema não é bom do ponto de vista de segurança, embora eu veja isso como algo que pressupõe um problema maior de permissões de acessoA DSL de consulta personalizada não tem a expressividade de coisas como
UNIQUEeCOUNTem SQL, então é preciso fazer algumas agregações manualmenteVi recentemente o Evolu https://www.evolu.dev/docs, e o escopo e os recursos parecem semelhantes; o Triplit tem
.subscribe()e o Evolu não, enquanto o Evolu usa SQL tipado baseado em Kysely, então as consultas são mais familiares e avançadas; no navegador, o Evolu usa SQLite sobre OPFS, enquanto o Triplit parece usar IndexedDBPost que publiquei no Reddit: https://www.reddit.com/r/sveltejs/comments/1dndpj8/cryptaa_a...
No lado das consultas, ainda não há agregações, mas isso está no roadmap; acho que, aproveitando o mecanismo de consultas incrementais, é possível seguir por caminhos bem interessantes
Por exemplo, em um dashboard de dados atualizado a cada hora, nos sistemas existentes (Postgres, MongoDB etc.) seria preciso reexecutar a consulta do zero toda vez, mas, com uma abordagem mais próxima do Materialize, processando apenas os novos dados, ele poderia continuar sendo atualizado de forma muito mais eficiente
Ainda não usei o Evolu diretamente, mas pode haver alguém no Discord que já tenha feito uma comparação: https://triplit.dev/discord
useQueryou por um modo separadoAo usar um banco de dados com um protocolo de sincronização offline tão bom, fico curioso sobre como lidar com a evolução do schema quando não é possível atualizar versões diferentes dos clientes ao mesmo tempo
Tenho o contexto de já ter sofrido com isso em um app móvel de saúde no passado
Se necessário, acaba-se fazendo escrita dupla nas duas versões ao mesmo tempo
É parecido com fazer uma migração ao vivo, sem downtime, de uma mudança incompatível em um banco SQL, mas o momento da transição depende do cliente, então é preciso manter essa lógica por mais tempo
Uma tabela que coordene a versão mais recente também é importante e, se houver uma mudança incompatível, clientes desatualizados devem pedir ao usuário para atualizar
Também dá para decidir por quanto tempo manter leitura/escrita dupla vinculando isso à versão mínima suportada em todos os clientes
O Triplit exibe um aviso quando você faz uma alteração que não é compatível retroativamente, como dá para ver na documentação: https://www.triplit.dev/docs/schemas/updating#pushing-the-sc...
Dito isso, com o tempo pode surgir naturalmente uma definição de schema bagunçada, cheia de nomes confusos
Ainda não lançamos uma solução para corrigir isso, mas estamos trabalhando em algumas coisas para tornar o processo menos doloroso; como pano de fundo para várias abordagens, o documento do Cambria é excelente: https://www.inkandswitch.com/cambria/
O usuário pode ter deixado o celular guardado em uma gaveta por 2 anos, então cada cliente deve migrar a si próprio o mais rápido possível
Isso evitaria que todo mundo tivesse que reinventar sua própria forma de gerenciar migrações
Não entendo bem em que tipo de app faz sentido o cliente poder escrever diretamente no banco de dados, nem como isso se sustenta sem lógica de backend
Tenho a mesma dúvida sobre Supabase e Firestore, então parece que estou deixando passar alguma coisa
No ambiente corporativo, é claro que é o contrário, e é frustrante ver discussões que ignoram isso
Especialmente no Twitter de tecnologia, quando vejo alguém defendendo uma stack ou uma forma de trabalhar, muitas vezes fica óbvio demais que a pessoa só fez CRUD e nunca criou sistemas de negócio; por isso ela não entende por que desenvolvedores experientes não concordam
Não acho que combine bem com algo que tenha muita lógica de backend
No Supabase, por exemplo, há um recurso chamado segurança em nível de linha
O cliente pode enviar uma requisição ao Supabase, mas o Supabase executa consultas adicionais no backend para decidir se a requisição recebida é permitida
Um exemplo simples: permitir ler, escrever e atualizar uma linha somente quando o valor da coluna
UserIDfor igual ao usuário autenticado que fez a requisiçãoTemos armazenado as configurações dos usuários no Triplit, e essas configurações precisavam poder ser gerenciadas por administradores
O usuário deve sentir que o app está sempre rodando localmente, e a qualidade da internet também costuma ser ruim; porém, como ele usa vários dispositivos e administradores precisam ver e gerenciar as configurações de outros usuários, era necessária sincronização
No geral, o Triplit tem sido excelente tanto na experiência de desenvolvimento frontend quanto no suporte; quando encontramos issues ou pedidos de funcionalidades, a equipe resolve tudo muito rapidamente
Quando houver uma resposta para deploy de alta disponibilidade, pretendemos mover também dados mais críticos para ele, em vez do Postgres
Fiquei curioso sobre por que escolheram a licença AGPL
Acho que vi a apresentação no YouTube https://www.youtube.com/playlist?list=PLTbD2QA-VMnXFsLbuPGz1... no servidor Discord Local First https://localfirstweb.dev/, então fiquei feliz em ver isso no Show HN
Como não uso TypeScript, talvez eu não seja o público-alvo principal; diferentemente da web, uso local-first sobretudo em apps móveis com conexões instáveis, com Flutter e backend em Rust
Outras soluções local-first, como ElectricSQL e PowerSync, sincronizam diretamente os DBs do cliente e do servidor, portanto são mais independentes de cliente/servidor
Soluções baseadas em CRDT também podem ser usadas no cliente e no servidor via FFI; por exemplo, o automerge é em Rust, então no Flutter dá para usá-lo via FFI com
flutter_rust_bridge, na web via WASM e no backend em RustO Triplit parece mais uma sincronização cliente-servidor mais clássica, tendo o servidor como fonte da verdade, em vez de uma resolução sem conflitos entre diferentes clientes
Fico curioso sobre por que escolheram uma solução em nível de linguagem, em vez de uma abordagem de camada de DB mais independente de cliente e servidor, e parece difícil dar suporte no futuro a linguagens e frameworks que não sejam baseados em JS
Além disso, parece que vocês querem competir com o Supabase, mas o Supabase também está experimentando sincronização em nível de DB do Postgres e CRDTs, então talvez consiga alcançar vocês https://news.ycombinator.com/item?id=33931971
Ainda assim, decidimos começar focando em TypeScript puro; acreditamos que o mercado é grande o suficiente, confiamos no futuro das PWAs e achamos que precisamos nos concentrar nisso para criar a melhor experiência
Em algum momento provavelmente criaremos algo mais independente de plataforma, mas o momento ainda não está claro
As equipes do ElectricSQL e do Supabase são excelentes e cuidadosas, e parecem que continuarão crescendo no espaço de SQL; essa é a diferença mais fundamental de abordagem
O Triplit acredita que, ao evitar SQL, pode oferecer a melhor experiência aos desenvolvedores, e há bastante espaço para as duas filosofias coexistirem
Se for LWW, fico curioso se a quantidade de informações no cliente cresce linearmente com o número de operações
Ou seja, quanto mais o usuário modifica o DB, o log de operações continua crescendo, ou há checkpoints? E como isso escala em termos de espaço quando um usuário faz milhões de operações por dia?
No entanto, o registro LWW em si não exige armazenar histórico; essa é apenas a implementação atual para permitir que clientes que ficaram muito tempo offline também sincronizem com eficiência
Ainda não dá para dizer que chegamos plenamente a um milhão de operações por dia, mas há a vantagem de o servidor ter autoridade
No futuro, o servidor Triplit poderá rastrear o timestamp da última sincronização de cada cliente e podar o histórico gradualmente, de modo semelhante a como o Postgres trata tuplas mortas com
VACUUMSeria bom ter bindings Rust para usar no Tauri
Com o crescimento do Tauri, o suporte a dispositivos móveis que está chegando e a popularidade recente do SQLite, isso poderia preencher a lacuna dos apps offline-first e se tornar a escolha padrão para muitas equipes de desenvolvimento
O ElectricSQL opera na camada de DB, por isso é independente de linguagem; como ele usa Rust no servidor, os bindings Rust poderiam funcionar tanto no cliente quanto no servidor
Se quiser desenvolver isso junto, seria ótimo me avisar
Se for o caso, o Triplit deve funcionar imediatamente
Tenho usado o Triplit há algum tempo em um app React Native e ele funciona muito bem
Recomendo muito; foi o único DB local-first que atendia a todos os meus requisitos
Tem uma linguagem de consulta adequada e sensata (não SQL), ótimo suporte a TypeScript, suporte offline, suporte a React Native, e também é bom que seja open source e possa ser auto-hospedado
Fico curioso se não é possível usá-lo junto com um DB PostgreSQL existente
Ainda não está pronta para ser divulgada, mas queremos permitir que as pessoas testem em breve
Eu também estou inclinado a usar essa opção