Bata no chão correndo com Vue.js e Firestore

Publicados: 2022-03-10
Resumo rápido ↬ Construir um MVP é construir muitas funcionalidades em um curto espaço de tempo. Vue.js é uma boa opção, com um mínimo de clichê arquitetônico e muito poder bruto. Tudo o que precisa é de um lugar para armazenar dados.

O Google Firebase tem uma nova possibilidade de armazenamento de dados chamada 'Firestore' (atualmente em fase beta), que se baseia no sucesso do Firebase Realtime Database , mas adiciona alguns recursos interessantes. Neste artigo, configuraremos os conceitos básicos de um aplicativo da Web usando Vue.js e Firestore.

Digamos que você tenha essa ótima ideia para um novo produto (por exemplo, o próximo Twitter, Facebook ou Instagram, porque nunca podemos ter muito social, certo?). Para começar, você quer fazer um protótipo ou M ínimo Produto Viável (MVP) deste produto. O objetivo é construir o núcleo do aplicativo o mais rápido possível para que você possa mostrá-lo aos usuários e obter feedback e analisar o uso. A ênfase está fortemente na velocidade de desenvolvimento e na iteração rápida.

Mas antes de começarmos a construir, nosso incrível produto precisa de um nome. Vamos chamá-lo de "Amazeballs". Vai ser legen - espere por isso - dary !

Aqui está uma foto de como eu imagino isso:

Captura de tela do aplicativo Amazeballs
O lendário aplicativo Amazeballs

Nosso aplicativo Amazeballs é – é claro – tudo sobre compartilhar petiscos bregas de sua vida pessoal com amigos, nos chamados Balls. No topo está um formulário para postar Bolas, abaixo estão as Bolas de seus amigos.

Ao construir um MVP, você precisará de ferramentas que lhe dêem o poder de implementar rapidamente os principais recursos, bem como a flexibilidade de adicionar e alterar recursos rapidamente mais tarde. Minha escolha recai sobre o Vue.js, pois é uma estrutura de renderização Javascript, apoiada pelo conjunto Firebase (do Google) e seu novo banco de dados em tempo real chamado Firestore.

Mais depois do salto! Continue lendo abaixo ↓

O Firestore pode ser acessado diretamente usando métodos HTTP normais, o que o torna uma solução completa de back-end como serviço, na qual você não precisa gerenciar nenhum de seus próprios servidores, mas ainda armazena dados online.

Parece poderoso e assustador, mas não se preocupe, vou guiá-lo pelas etapas de criação e hospedagem deste novo aplicativo da web. Observe o tamanho da barra de rolagem nesta página; não há uma grande quantidade de etapas para isso. Além disso, se você quiser saber onde colocar cada um dos trechos de código em um repositório de código, poderá ver uma versão totalmente em execução do Amazeballs no github.

Vamos começar

Estamos começando com o Vue.js. É ótimo para iniciantes em Javascript, pois você começa com HTML e gradualmente adiciona lógica a ele. Mas não subestime; ele contém muitos recursos poderosos. Essa combinação o torna minha primeira escolha para um framework front-end.

Vue.js tem uma interface de linha de comando (CLI) para projetos de scaffolding. Usaremos isso para obter a configuração básica rapidamente. Primeiro, instale a CLI e use-a para criar um novo projeto baseado no modelo “webpack-simple”.

 npm install -g vue-cli vue init webpack-simple amazeballs

Se você seguir os passos na tela ( npm install e npm run dev ) um navegador será aberto com um grande logotipo Vue.js.

Parabéns! Essa foi fácil.

Em seguida, precisamos criar um projeto do Firebase. Acesse https://console.firebase.google.com/ e crie um projeto. Um projeto começa no plano Spark gratuito, que oferece um banco de dados limitado (1 GB de dados, 50 mil leituras por dia) e 1 GB de hospedagem. Isso é mais do que suficiente para o nosso MVP e facilmente atualizável quando o aplicativo ganha força.

Clique em 'Adicionar Firebase ao seu aplicativo da web' para exibir a configuração necessária. Usaremos essa configuração em nosso aplicativo, mas de uma maneira agradável do Vue.js usando o estado compartilhado.

Primeiro npm install firebase , depois crie um arquivo chamado src/store.js . Este é o local em que vamos colocar o estado compartilhado para que cada componente Vue.js possa acessá-lo independentemente da árvore de componentes. Segue abaixo o conteúdo do arquivo. O estado contém apenas alguns espaços reservados por enquanto.

 import Vue from 'vue'; import firebase from 'firebase/app'; import 'firebase/firestore'; // Initialize Firebase, copy this from the cloud console // Or use mine :) var config = { apiKey: "AIzaSyDlRxHKYbuCOW25uCEN2mnAAgnholag8tU", authDomain: "amazeballs-by-q42.firebaseapp.com", databaseURL: "https://amazeballs-by-q42.firebaseio.com", projectId: "amazeballs-by-q42", storageBucket: "amazeballs-by-q42.appspot.com", messagingSenderId: "972553621573" }; firebase.initializeApp(config); // The shared state object that any vue component can get access to. // Has some placeholders that we'll use further on! export const store = { ballsInFeed: null, currentUser: null, writeBall: (message) => console.log(message) };

Agora vamos adicionar as partes do Firebase. Um trecho de código para obter os dados do Firestore:

 // a reference to the Balls collection const ballsCollection = firebase.firestore() .collection('balls'); // onSnapshot is executed every time the data // in the underlying firestore collection changes // It will get passed an array of references to // the documents that match your query ballsCollection .onSnapshot((ballsRef) => { const balls = []; ballsRef.forEach((doc) => { const ball = doc.data(); ball.id = doc.id; balls.push(ball); }); store.ballsInFeed = balls; });

E então substitua a função writeBall por uma que realmente execute uma escrita:

 writeBall: (message) => ballsCollection.add({ createdOn: new Date(), author: store.currentUser, message })

Observe como os dois estão completamente desacoplados. Quando você insere em uma coleção, o onSnapshot é acionado porque você inseriu um item. Isso torna o gerenciamento de estado muito mais fácil.

Agora você tem um objeto de estado compartilhado que qualquer componente Vue.js pode acessar facilmente. Vamos usá-lo bem.

Postar coisas!

Primeiro, vamos descobrir quem é o usuário atual.

O Firebase tem APIs de autenticação que ajudam você com o trabalho de conhecer seu usuário. Ative os apropriados no Firebase Console em AutenticaçãoMétodo de login . Por enquanto, vou usar o Login do Google — com um botão nada sofisticado.

Captura de tela da página de login com autenticação do Google
Autenticação com o Login do Google

O Firebase não oferece nenhuma ajuda na interface, então você terá que criar seus próprios botões “Login com Google/Facebook/Twitter” e/ou campos de entrada de nome de usuário/senha. Seu componente de login provavelmente será um pouco assim:

 <template> <div> <button @click.prevent="signInWithGoogle">Log in with Google</button> </div> </template> <script> import firebase from 'firebase/app'; import 'firebase/auth'; export default { methods: { signInWithGoogle() { var provider = new firebase.auth.GoogleAuthProvider(); firebase.auth().signInWithPopup(provider); } } } </script>

Agora há mais uma peça do quebra-cabeça de login, e isso é obter a variável currentUser na loja. Adicione estas linhas ao seu store.js :

 // When a user logs in or out, save that in the store firebase.auth().onAuthStateChanged((user) => { store.currentUser = user; });

Devido a essas três linhas, toda vez que o usuário conectado no momento é alterado (log in ou out), store.currentUser também muda. Vamos postar algumas Bolas!

Captura de tela da opção de logout
O estado de login é armazenado no arquivo store.js

O formulário de entrada é um componente Vue.js separado que está conectado à função writeBall em nossa loja, assim:

 <template> <form @submit.prevent="formPost"> <textarea v-model="message" /> <input type="submit" value="DUNK!" /> </form> </template> <script> import { store } from './store'; export default { data() { return { message: null, }; }, methods: { formPost() { store.writeBall(this.message); } }, } </script>

Impressionante! Agora as pessoas podem fazer login e começar a postar bolas. Mas espere, está faltando autorização. Queremos que você só possa postar Balls sozinho, e é aí que entram as Regras do Firestore . Elas são compostas de código Javascript que define privilégios de acesso ao banco de dados. Você pode inseri-los por meio do console do Firestore, mas também pode usar a Firebase CLI para instalá-los a partir de um arquivo no disco. Instale e execute-o assim:

 npm install -g firebase-tools firebase login firebase init firestore

Você receberá um arquivo chamado firestore.rules onde poderá adicionar autorização para seu aplicativo. Queremos que cada usuário possa inserir suas próprias bolas, mas não inserir ou editar as de outra pessoa. O exemplo abaixo funciona bem. Ele permite que todos leiam todos os documentos do banco de dados, mas você só pode inserir se estiver logado, e o recurso inserido tiver um campo “autor” igual ao do usuário atualmente logado.

 service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read: if true; allow create: if request.auth.uid != null && request.auth.uid == request.resource.data.author; } } }

Parece apenas algumas linhas de código, mas é muito poderoso e pode se tornar complexo muito rapidamente. O Firebase está trabalhando em ferramentas melhores para essa parte, mas, por enquanto, é tentativa e erro até que se comporte da maneira que você deseja.

Se você executar o firebase deploy , as regras do Firestore serão implantadas e protegerão seus dados de produção em segundos.

Adicionando lógica de servidor

Na sua página inicial, você deseja ver uma linha do tempo com as bolas de seus amigos. Dependendo de como você deseja determinar quais Balls um usuário vê, realizar essa consulta diretamente no banco de dados pode ser um gargalo de desempenho. Uma alternativa é criar uma Firebase Cloud Function que é ativada em cada Ball postada e a anexa às paredes de todos os amigos do autor. Dessa forma, é assíncrono, não bloqueante e eventualmente consistente. Ou em outras palavras, chegará lá.

Para manter os exemplos simples, farei uma pequena demonstração de ouvir Balls criadas e modificar sua mensagem. Não porque isso seja particularmente útil, mas para mostrar como é fácil colocar as funções de nuvem em funcionamento.

 const functions = require('firebase-functions'); exports.createBall = functions.firestore .document('balls/{ballId}') .onCreate(event => { var createdMessage = event.data.get('message'); return event.data.ref.set({ message: createdMessage + ', yo!' }, {merge: true}); });

Oh, espere, eu esqueci de dizer onde escrever este código.

 firebase init functions

Isso cria o diretório de funções com um index.js . Esse é o arquivo no qual você pode escrever suas próprias Cloud Functions . Ou copie e cole o meu se estiver muito impressionado com ele.

O Cloud Functions oferece um bom local para separar diferentes partes do seu aplicativo e fazer com que elas se comuniquem de forma assíncrona. Ou, em estilo de desenho arquitetônico:

Diagrama de arquitetura da lógica do servidor do Cloud Functions
Comunicação assíncrona entre os diferentes componentes do seu aplicativo

Última etapa: implantação

O Firebase tem sua opção de hospedagem disponível para isso e você pode usá-la por meio da Firebase CLI.

 firebase init hosting

Escolha dist como um diretório público e depois 'Sim' para reescrever todos os URLs para index.html . Esta última opção permite que você use o vue-router para gerenciar URLs bonitas em seu aplicativo.

Agora há um pequeno obstáculo: a pasta dist não contém um arquivo index.html que aponte para a compilação correta do seu código. Para corrigir isso, adicione um script npm ao seu package.json :

 { "scripts": { "deploy": "npm run build && mkdir dist/dist && mv dist/*.* dist/dist/ && cp index.html dist/ && firebase deploy" } }

Agora, basta executar npm deploy e a Firebase CLI mostrará o URL do seu código hospedado!

Quando usar esta arquitetura

Esta configuração é perfeita para um MVP. Na terceira vez que fizer isso, você terá um aplicativo da Web funcionando em minutos — apoiado por um banco de dados escalável hospedado gratuitamente. Você pode começar a criar recursos imediatamente.

Além disso, há muito espaço para crescer. Se o Cloud Functions não for poderoso o suficiente, você poderá recorrer a uma API tradicional em execução no docker no Google Cloud, por exemplo. Além disso, você pode atualizar sua arquitetura Vue.js com vue-router e vuex e usar o poder do webpack incluído no modelo vue-cli.

Mas nem tudo são arco-íris e unicórnios. A advertência mais notória é o fato de que seus clientes estão imediatamente conversando com seu banco de dados. Não há camada de middleware que você possa usar para transformar os dados brutos em um formato mais fácil para o cliente. Portanto, você deve armazená-lo de maneira amigável ao cliente. Sempre que seus clientes solicitarem alterações, será muito difícil executar migrações de dados no Firebase. Para isso, você precisará escrever um cliente Firestore personalizado que leia todos os registros, os transforme e os grave de volta.

Reserve um tempo para decidir sobre seu modelo de dados. Se você precisar alterar seu modelo de dados posteriormente, a migração de dados é sua única opção.

Então, quais são os exemplos de projetos usando essas ferramentas? Entre os grandes nomes que usam Vue.js estão Laravel, GitLab e (para os holandeses) nu.nl. O Firestore ainda está na versão beta, portanto, ainda não há muitos usuários ativos, mas o conjunto do Firebase já está sendo usado pela National Public Radio , Shazam e outros. Vi colegas implementarem o Firebase para o jogo Road Warriors, baseado em Unity, que foi baixado mais de um milhão de vezes nos primeiros cinco dias. Pode levar bastante carga e é muito versátil com clientes para web, dispositivos móveis nativos, Unity e assim por diante.

Onde eu assino?!

Se você quiser saber mais, considere os seguintes recursos:

  • Amostra de trabalho contendo todo o código acima
  • Documentação em Vue.js, vue-router, vue-cli
  • Documentação no Firebase
  • Uma maneira divertida de conhecer melhor o Firebase — o blog do YouTube

Boa codificação!