Na semana passada eu trouxe um vídeo aqui pro canal do YouTube da Rocket City, onde eu começo a falar e introduzir um assunto que tem me despertado interesse nos últimos meses, que são o Local First apps e offline First apps. Caso você ainda não entenda, não compreenda esses dois conceitos, eu vou tentar resumir de uma maneira bem rápida. Local First apps são aplicações web em que você copia uma parte dos dados que estão no banco de dados em produção para dentro do navegador que tá utilizando a sua aplicação.
E dessa forma o usuário consegue ter um acesso muito mais ágil aos dados. além de poder filtrar esses dados, poder adicionar, modificar esses dados e depois esses dados são sincronizados com o seu banco de dados online. E é claro que isso, da maneira mais simples, quando você ouve parece que introduz um monte de problemas de segurança, onde o usuário vai poder acessar dados que não são deles ou vai poder modificar dados que não são deles ou onde você vai daqui a pouco ter problemas de conflito de pessoas alterando a mesmos os mesmos dados.
E calma que, na verdade, todas essas tecnologias que eu vou citar aqui nesse vídeo já visam resolver esses problemas. E isso na verdade é uma das primeiras coisas resolvidas por qualquer uma das ferramentas que visa lidar com essa parte de local first, que são as ferramentas que estão surgindo agora e que eu citei no último vídeo, como por exemplo o Zero, o Electric SQL, o Auto Merge, a gente tem o Tiny Base, a gente tem o Power Sync, a gente tem o o TCK DB que tá vindo agora também. Então tem muitas ferramentas que estão entrando nessa ideia, nesse mercado.
Então eu, se fosse você, dev frontend fck, não é para sair estudando que nem um maluco ou utilizando qualquer das coisas que eu vou mostrar aqui já em produção, em alguma aplicação, mas ficaria de olho porque tem muita movimentação indo para esse lado. E aí, ao mesmo tempo que a gente tem os local first apps, né, os apps que funcionam primeiramente local e depois são sincronizados os dados em produção, nós temos também offline first. Não necessariamente todo app local first é offline first.
O que caracteriza um appline first é um app que funciona sem internet, ou seja, ele tem a quantidade de dados necessária para funcionar armazenadas em um storage local, geralmente um index db. Ou você pode utilizar um Web assembly ali rodando um posts na sua máquina usando um PG Lite, por exemplo. Pode usar um local storage, pode usar um arquivo físico.
Você tem inúmeras maneiras de fazer isso. Mas hoje a gente ainda não vai entrar no offline first, porque os meus estudos aí dos últimos 2 tr dias foi tentando desenvolver um protótipo, incluindo duas das ferramentas que vocês mais colocaram no comentário aí dos últimos vídeos, na verdade do último vídeo, né, que foram o electric SQL e o T Stack DB. Então, eu criei aqui um projetinho funcionando em local first, usando essas duas ferramentas, mas lembrando, ambas as ferramentas não estão prontas paraa produção.
Então, isso aqui é um vídeo totalmente experimental, como eu já coloquei em outros vídeos do YouTube, coloco nos comentários de vez em quando, porque vocês me enchem o meu saco. Todos os vídeos que eu posto aqui, na maioria das vezes, eles são vídeos entusiastas, são vídeos experimentais. Se você quer aprender a programação para entrar no mercado, então você vai na plataforma da Rocket City.
Aqui no YouTube a gente fala sobre tudo um pouco, ok? Então não vem me incomodar aqui nos comentários falando que eu tô ensinando mais uma ferramenta JavaScript que a pessoa o pessoal vai sair utilizando e que não sei o quê. Já vou avisando.
Todas as ferramentas que eu tô mostrando aqui, elas estão em beta, alfa, sei lá o qu. Então não, não dá para usar em produção ainda, principalmente o tanto stack deb que nem documentação ele tem ainda, então eu tive que entrar ali na documentação de dentro do GitHub, tentar encaixar ali. Muita coisa não existe ainda, então eu fui indo pela tipagem do Typescript.
Mas eu vou te mostrar como que isso aqui tá funcionando e como que ficou esse meu projetinho. Botei a tela preta do nada aí, mas vamos lá, né? Primeiramente a gente tem, como eu falei aqui, ah, e eu postei aqui até eu tô postando um pouquinho desse desse progresso com essa aplicação a Local First, offline first, lá no meu Twitter.
Então, caso você queira ficar por dentro aí dos próximos passos, é só acompanhar por lá. E aí, vamos entender um pouquinho, né? A primeira coisa, o que que eu fiz, né?
A gente, eu utilizei duas ferramentas aqui para criar esse projeto. Uma delas, a primeira, sendo o electric SQL. Caso você não saiba, o Electric SQL, ele é basicamente uma ferramenta que você roda junto com o seu banco de dados, que ela que ela é feita em elixir, que com certeza é uma das melhores linguagens para isso, porque elixir é uma das melhores linguagens pra gente trabalhar com aplicações em real time, né, que precisa uma latência baixíssima.
E o electric, ele basicamente é hospedado em algum servidor muito próximo, geralmente na mesma região do seu banco de dados. E a sua aplicação, ela utiliza o electric, né, essa instância que tá rodando ali, para poder acessar essas informações do banco de dados que precisam ser copiadas pra máquina do usuário. E aí, claro que a gente pode definir quais informações o usuário vai poder copiar pra máquina dele para que então a aplicação comece a funcionar utilizando esse conceito de local first.
Tem toda uma gama de documentação de documentação voltada à parte de segurança. Aqui dentro de security, mas principalmente aqui dentro de Health, que são os dois modelos de autenticação que o Electric usa para determinar o que que o usuário tem acesso, o que que o usuário pode copiar pro navegador dele para que então a aplicação comece a funcionar como local first. E eu ainda não consegui entrar em tudo isso.
Eu tô há dois dias mexendo nisso, então é pouco tempo ainda, mas quem sabe nos próximos vídeos eu já trago aqui um pouco mais de detalhes sobre isso, tá? Inclusive os meus dois próximos passos aqui é justamente essa parte de autenticação, então determinar o que que cada usuário vai poder acessar, porque por enquanto ele pode acessar tudo, né? E não é muito legal isso.
E outra coisa é a parte de offline first. Então eu já tô trabalhando aqui dentro até inclusive nesse guia de rights aqui embaixo a gente vem esse through the database e ele explica como que a gente pode usar o PG Lite que é basicamente uma versão do Postgress que roda em Web assembly onde a gente pode rodar no navegador, ou seja, é um PSTQ completo rodando no navegador do usuário. Ou seja, a gente tem uma um banco de dados posts no nosso servidor e nós temos um banco de dados postques em cada navegador de cada usuário que tá acessando a nossa aplicação.
E com isso a gente consegue copiar esses dados para dentro do postcs do navegador ali do usuário e trabalhar com esses dados, executar SQL diretamente neste banco de dados. E assim a gente tem uma aplicação funcionando totalmente offline. E quando qualquer alteração é feita dentro desse PGLite, dentro desse postcas alterações são sincronizadas com o banco online.
É claro que os casos de uso paraa aplicação offline eles são pequenos, são mínimos, mas tenho certeza que você utiliza algumas dessas aplicações no seu dia a dia, como por exemplo o próprio Notion, o próprio Linear. Existem aplicações que se beneficiam muito desse conceito, mas novamente lembrando, não é para toda a aplicação. Tudo isso aqui não passa de um grande entusiasmo da minha parte e também talvez da sua em conhecer mais esse modelo de trabalhar com dados dentro das nossas aplicações.
E aí vamos entender um pouquinho, né? Então, electric, beleza? E nós estamos também utilizando aqui o TSAC DB.
Como eu falei, uma aplicação ainda que tá em alfa, do beta, do sei lá o que, das quantas, tá? Então ela ainda não é recomendada pra produção. E o T Stack DB, ele nada mais é do que uma biblioteca pra gente integrar com o nosso projeto React.
Eu acho que por enquanto só React, eu não vi ainda hooks para outras ferramentas, que permite a gente se comunicar com o Electric de uma maneira mais legal, tá? Então ele basicamente fornece uma API pros nossos componentes React consumirem dados desse nosso banco de dados em produção e armazenarem em uma estrutura normalizada dentro do frontend. O que que é uma estrutura normalizada?
Imagina que você tem, por exemplo, um arrei de issues, por exemplo, levando por esse lado de do liner, né? Então, um arrei de toos. E esses toos, eles têm ali quem criou esses toos.
Então, a gente tem ali, por exemplo, o nome do usuário. Ao mesmo tempo, numa outra página da nossa aplicação, a gente consegue ver uma listagem de usuários do nosso time. Entende que esse usuário que a gente mostra dentro do To-Do e essa lista de usuários que a gente mostra em uma outra página da nossa aplicação, se tratam da mesma entidade?
Se tratam de usuários. Ou seja, se em algum momento o nome do usuário Diego mudar ali na lista de usuários, esse Diego deve refletir também dentro dos usuários que estão armazenados nos nossos toos. Isso a gente chama de normalização.
Normalização de dados é você pegar dados que são a mesma coisa e armazená-los em um único local e não ficar replicando as informações em vários locais. Então é mais ou menos o que a gente faz com o banco de dados. Se a gente tem uma tabela no banco de dados de todoo, por exemplo, eu não armazeno ali o nome do usuário que pertence à aquele todo.
Ali dentro da tabela. Eu crio um user ID e faço uma referência para uma outra tabela. Isso é normalização.
É basicamente a gente separar de uma forma que a gente consiga correlacionar os dados e não repetir esses dados. Então o T Stack DB ajuda a gente a fazer isso no front end banco de dados. Isso é bem legal.
Então a gente consegue fazer joint, dados e tudo mais, da mesma maneira que a gente faz num SQL. E aí eu vou te mostrar aqui. Claro que eu não vou criar esse projeto do zero porque assim, deu muita dor de cabeça e eu nem sei se eu consigo chegar no mesmo local que eu cheguei agora de novo, mas eu compartilho o GitHub com vocês.
E o projetinho ele é basicamente isso. A gente tem aqui uma lista de issues e eu posso adicionar uma nova isso. Então isso aqui é um protótipo de início de um linear, digamos assim, pra gente entender como que funciona tudo isso de local first.
Tá entendendo o que que tá acontecendo aqui? Quando eu acesso essa minha aplicação, a minha aplicação web vai lá, bate na minha API, busca todas as issues que eu tenho acesso e guarda no meu navegador em memória. Por quê?
Porque a minha aplicação ainda não é offline first, então ela não guarda num index dB, num local storage ou qualquer coisa assim. Ela é local first. Que que quer dizer com isso, né?
Depois que eu tenho as issues na minha máquina, toda issue que eu alterar, deletar ou adicionar vai acontecer primeiro na minha máquina. Primeiro aqui na minha máquina, né, no meu na minha memória. E depois isto vai ser sincronizado com o servidor e com os demais usuários.
Só que isso aqui é muito legal porque a partir do momento que a gente tem o electric operando ali, e o Electric ele é a porta de entrada, né, na verdade a porta de saída de dados do meu postcs e eu não tô fazendo o meu front end acessar diretamente o postcs, isso permite com que se eu fizer uma alteração numa isso, então olha só, vamos abrir aqui o meu banco de dados, então vou pegar aqui o o postico, que é o que eu uso. Ah, eu não lembro qual que é a minha senha do banco de dados. Deixa eu ver aqui.
Tô usando password. surto. E aí eu venho aqui na minha nas minhas issos.
Então veja que esta primeira issue que eu tenho aqui em cima, deixa eu aumentar um pouquinho, tirar essa barrinha. Essa primeira issue aqui, ó, é esta issu que tá sendo mostrada aqui em tela, certo? Então, ó, se eu dou um F5 aqui, F5 aqui, a gente tem a isso.
Veja que se eu troco o título, o título dela, então, olha só, primeira issue e dou enter e dou um save, isso altera instantaneamente em todos os usuários que estão conectados à minha aplicação. Então essa é a questão mais legal. Quando a gente trabalha com uma aplicação local first, a nossa primeira responsabilidade é trazer esse sincronismo para todos os clientes.
Então, quando você trabalha lá com Notion da vida ou se você já utilizou o Liner, você percebe que cada alteração que você faz reflete para todo mundo que tá acessando aquela aplicação em tempo real. E aqui o electric ele usa um sistema de stream, ele não utiliza, se eu não me engano, web sockets, pelo que eu vi, tá? Ele pode utilizar talvez para alguma outra coisa, mas para essa parte de alterações ele utiliza streams ou até para quem gosta mais aí um server sents, né?
Por quê? Porque é uma estratégia muito mais performática. Que que isso quer dizer?
Quando a gente busca as issues ali de dentro do electric, né, que conecta com o nosso post e traz essa lista, a gente na verdade mantém conexão em aberto que tá enviando dados conforme eles são alterados dentro do banco de dados. E essa conexão fica ouvindo alterações dentro do PostC. E qualquer alteração nessas issues que a gente tem acesso são retornadas através dessa stream que tá em aberto.
Ou seja, não é um web socket que tá transcionando mensagens e sim uma conexão HTTP que retorna chunks ali de dados com cada alteração que vai acontecendo dentro do postc, tá? E aí vamos entender como é que isso aqui funciona dentro do código, tá? Eu vou mostrar primeiro o back end e depois a gente vai pro front end.
O meu backend basicamente o que que eu fiz, né? Subi aqui um PostC e subi um electric SQL, apontando aqui pra conexão do Postcr que tá rodando dentro da minha máquina. E por enquanto usei o Electric Insecure, que basicamente permite que a gente possa acessar qualquer dado do banco de dados, que não é o que a gente vai fazer em produção, com certeza, né?
E aí eu tô utilizando aqui também o Drzzle para fazer a parte de criação das tabelas do banco de dados. Então eu criei basicamente duas tabelas, uma tabela de issues ali dentro do postc, tá? Que tem alguns dados aqui.
E ela tem uma referência para users, que é uma outra tabela users, onde a gente tem então essas duas tabelas no nosso esquema do banco de dados, né? Criei essas duas tabelas aqui, né? Criei as migrations e também criei um seed para pré-popular o meu banco de dados com 10.
000 issues e cinco usuários. Além disso, aqui no meu backend, se eu quisesse apenas acessar os dados, era tudo o que eu precisava. Isso aqui é tudo o que eu preciso.
Agora, como todo backend, eu vou precisar, na verdade, né, de mutações, de inserção, de updates e deletes. Eu vou criar uma API normal a partir desse momento. Então isso é uma outra coisa que o pessoal me perguntou muito no último vídeo, que é, Diego, a partir do momento que eu tenho uma aplicação local first, as alterações acontecem local e a partir disso elas são sincronizadas com o banco e pronto, eu não preciso mais ter uma API pra crude, né, pra parte de create, update, delete e tudo mais.
E não, na verdade, as alterações que acontecem local, elas são muito mais apenas pro usuário que tá ali alterando. Então, a gente chama isso de interface otimista. Então, lá no Liner, por exemplo, quando eu altero o título de uma ISU, isso acontece instantaneamente para mim, pros outros usuários tem uma latência, porque isso precisa sim fazer uma operação na nossa pay.
Então aqui, ó, veja que paraa parte de criação de uma ISU, eu continuo tendo uma rota post normal, tradicional, que faz uma operação dentro do banco de dados normal. Só qual que é a parte mais legal aqui? Quando essa operação aqui acontece, o insert no banco de dados acontece, isso é propagado para todos usuários que têm acesso a essa isso, que deveriam ter acesso a essa isso.
Ou seja, se eu tenho 40 usuários usando minha aplicação, que estão ali listando as issues e isso tá na tela deles, no momento que houver esse insert, isso é refletido a todos os usuários que estão utilizando a minha aplicação, OK? Isso é muito legal. E aí, saindo aqui um pouco do back end, indo lá pro nosso front end.
Deixa eu acessar aqui no nosso front end, que que eu fiz, tá? E aí o nosso front, o meu front end ele tá um pouco bagunçado porque tem algumas coisas já que eu até coloquei numa pastinha feature aqui, ó, que é o que eu tava testando. Te mostro também, mas eh não tá funcionando ainda.
Mas eu basicamente aqui criei um único componente aqui, né, que é o app da nossa aplicação. E aí a primeira coisa que a gente tem que fazer aqui no nosso front endar ele com o electric, que é quem traz os dados, né? Isso a gente faz através de collections.
Então, veja só, eu crio uma collection de issue, né? Então, eu tenho aqui que é basicamente a representação de um subset de dados. Então, não é cada tabela do banco uma collection no meu front end, não.
Uma collection ela é uma representação de uma parte dos dados que eu tô trazendo lá de dentro do meu banco de dados. Então, imagina, por exemplo, que eu tenho uma tela na minha aplicação que eu mostro somente as issues as quais eu estou assinado para resolver, qual eu eu estou responsável por elas. Então eu criaria uma collection aqui, na verdade chamada my issues, por exemplo, ou issues I am responsible of, sei lá, qualquer coisa assim.
Mas basicamente a collection ela é uma representação de um subset de dados e não de uma tabela necessariamente. Mas aqui, como eu queria mostrar todas as isses, então eu criei basicamente uma collection chamada issues e aí eu passo a URL onde tá operando o electric SQL. Então isso aqui não é a URL do meu node ali do bom backend e sim do electric SQL.
passo o nome da tabela. E aqui eu poderia passar, por exemplo, um wear, poderia passar quais colums eu quero trazer. E aí, novamente, nossa, Diego, isso é muito ant segurança, eu posso passar um we e fazer uma consulta no banco.
Não, cara, pelo amor de Deus. Isso aqui é basicamente o que eu posso fazer dentro do que o electric me permite. Ou seja, lá no Electric a gente configura, olha, o usuário vai ter que enviar um JWT aqui.
Então, por exemplo, a gente consegue mandar aqui nos headers um JWT. E aí eu falo lá no Elric, olha, o usuário só vai poder buscar esse os dados os quais esse user ID que tá no JWT dele tem acesso e aí eu determino lá quais dados ele tem acesso. Então é basicamente uma forma a aqui a gente ainda tá lidando com uma forma insegura, mas a gente com certeza vai ter que trabalhar com segurança.
Indico qual que é a primary key e indico qual que é o meu esquema de dados. E aqui eu tô utilizando o Zot. Uma coisa legal que eu achei aqui e já antecipando, né, o Esquema, se eu passar o mouse aqui em cima dele, ó, ele já usa o novo formato do standard skima.
Caso você viu o meu vídeo sobre ZOD versão 4 aqui no canal, que eu aconselho você ver, o Standard Skema é uma iniciativa da comunidade, as bibliotecas de tipagem mais famosas, de criar uma única tipagem para todas as bibliotecas de validação. Que que isso quer dizer? Quer dizer que se eu não quero usar o zod aqui, quero usar um archetype, quero usar um valebot, não tem problema, vai funcionar normal, porque a tipagem interna dessas lips é totalmente igual.
Isso é bem bacana. E aí eu tenho também aqui uma issue pra parte de users. Veja que ela é extremamente semelhante, certo?
E aí uma das coisas importantes é que aqui nas issues nós temos a referência pro user ID. E aí, como é que eu faço para buscar, né? Como é que eu fiz para buscar esses dados aqui dentro?
Olha que fácil, lá no meu app eu utilizei aqui do TC DB um carinha chamado Use Live Query, né? Live vende ao vivo, ou seja, real time. Quer dizer que eu estou basicamente acessando esses dados aqui do meu banco de dados e que quando qualquer alteração que esteja dentro desse escopo acontecer, eu vou mostrar esses dados em tempo real na minha tela.
Então, olha só, eu estou buscando das minhas issues fazendo um join, né, um inner join aqui, para quem gosta de SQL, já tá bem acostumado com isso aqui, dando um select nesses campos aqui, limitando os dados em cinco, fazendo uma ordenação e dando um key buy. O keyby ele é basicamente uma forma de eu dizer qual que é o campo único em cada item das iss aqui para eu conseguir, a gente sabe para que que aqui funciona no React, para eu conseguir distinguir quando que veio um item novo ou não veio, né? E aí, como você pode ver aqui em tela, eu tenho cinco issos sendo mostradas aqui, porque como eu fiz, eu limitei por cinco, né?
Então isso tá acontecendo aqui em tempo real. E aí o próximo passo que eu tô percorrendo aqui dentro, e aí como eu falei, né, tudo isso aqui ainda é muito experimental, é muito mais uma diversão para mim, talvez para você também, se não for, tudo bem. Mas o que que eu tô fazendo aqui agora é trabalhar com a parte de mutations, né?
Porque vamos lá, né? Se eu quero adicionar uma nova issue aqui, eu poderia ir por esse modelo tradicional de fazer uma requisição HTTP. Então, deixa eu comentar aqui meu código.
Ó, eu tenho uma função aqui, ó, create issue, né? Eu poderia simplesmente vir aqui nela, fazer uma requisição http lá pro meu backend, enviando aqui o meu title. Então, olha só, pego, passo title aqui dentro, faço uma requisição aqui na rota ah, um método post, na rota issues que a gente criou lá no nosso backend, né?
E aí, que que eu faço aqui, ó? Vem aqui em cima e a gente pode vir aqui e escrever. Só que aqui ela tá ordenando, ó.
Vamos ordenar pelo ID de forma decrescente, ó. Então aqui tá a última, né? A 10.
000. E aí eu venho aqui e adiciono uma nova isso. Então dou um cree.
Veja que ele não fez nada. Deixa eu dar um F5 aqui. [Música] Ã a fat issues.
Meu backend tá rodando. Tá. Deixa eu ver se a requisição tá acontecendo aqui.
É porque eu modifiquei muita coisa no meu código. Eu já não sei como que tá, que que tá certo, que que não tá. Então tá dando erro aqui, ó.
Erro 500. Duplicate key violates unique constraint issues pick. Tá, isso aqui acontece porque o drizzle seed, né?
Usei o seed do drizzel para pré-popular os meus dados no banco de dados. E acontece que quando eu tenho um campo serial do Postgris, que é aquele auto increment, o Drzzel não pegou e aumentou esse auto increment. Então, quando ele tá inserindo a ISU aqui, ele tá tentando inserir uma ISU com ID1, mas já existe uma ISU com ID1, né?
Então, se eu não me engano, eu até tinha deixado aqui uma querer para ele eh atualizar esse valor pro último ID. E agora quando eu faço a criação da isso, ó, e d create, pronto, veja que ela já refletiu tanto no primeiro cliente que tá conectado quanto no segundo. E por eu estar em local host, isso é muito rápido, mas para quem sabe, né, e quem tá acostumado, quando a gente trabalha em produção, a gente tem uma latência, mesmo que seja uma latência mínima.
Então, geralmente, para eu dar uma sensação de tempo real para ser muito rápido, eu trabalho com mutações otimistas ou interface otimista, que a gente chama, que é eu mostrar pro usuário que a ação dele deu certo, mesmo antes de ter dado realmente certo. Isso acontece, por exemplo, no Instagram. Se você tiver sem internet, for lá no Instagram, d curtir uma foto, vai aparecer ali que o seu curtir funcionou, mas não funcionou porque não foi enviado, você tá sem internet.
Só que isso a gente chama de interface otimista. a gente mostra que deu certo mesmo sem dar certo. Depois, se não der certo, a gente volta atrás.
E eu tava começando a implementar isso no meu código, utilizando aqui o hook, né, do TCK DB chamado Use Optimistic Mutation. Mas, cara, como isso aqui tá com pouca documentação e muito bug ainda, principalmente na parte de tipagem assim do Typecript, ainda tem muita coisa que precisa ser melhorado. Eu dei uma empacada boa nisso aqui hoje assim e ele meio que funcionou, mas não funcionou.
E por isso que eu nem quis trazer aqui para eu continuar estudando isso e quem sabe trazendo num próximo vídeo. Mas assim, eu tô bem contente para onde que esse app tá seguindo aqui. E claro, ainda são tudo coisas experimentais e coisas que eu tô testando, mas eu não queria deixar de trazer um update para você aqui, já que o último vídeo que eu trouxe aqui no YouTube deu bastante gente assistindo, bastante gente comentando sobre esse assunto e pedindo para eu trazer um projetinho mais completo.
E eu ainda quero trazer um projetinho mais completo, que tenha talvez um formato até de aula assim de construção, só que ainda não dá. Sendo bem sincero, as bibliotecas elas estão muito embrionárias ainda, principalmente o Tstack DB, né? O Electrake ele já tá um pouquinho mais legal, mas aí a parte do cliente já não fica tão legal que tudo isso ainda tem que aguardar um pouquinho mais.
Mas eu espero que você tenha curtido pelo menos esse exemplo de projeto que eu te mostrei e vai aqui embaixo nos comentários e diz o que que tá te eh chamando mais atenção, te interessando mais nesse ecossistema e a gente vai conversando para quem sabe trazer mais vídeos no futuro sobre esse assunto, beleza? Vou ficando por aqui, um grande abraço e tchau, até quinta-feira.