Firebase com Vue.js: ligar o Firestore a uma aplicação Vue 3

O Firebase é uma das formas mais simples de montar uma base de dados alojada em tempo real. O VueFire torna a coisa ainda mais fácil: liga diretamente os objetos Firebase ao sistema de reatividade do Vue e mantém-nos atualizados. Juntos, o resultado é notavelmente simples

Firebase com Vue.js: ligar o Firestore a uma aplicação Vue 3
Resposta rápida

Instala o firebase e o vuefire, inicializa a aplicação com initializeApp e getFirestore, depois lê uma coleção com useCollection: a lista atualiza-se sozinha. Desenvolve contra o emulador Firestore local em vez do projeto de produção.

O Firebase fornece uma base de dados, uma autenticação e um alojamento sem que tenhas de escrever um servidor. Ligado ao Vue, dá uma base em tempo real cujas alterações se propagam sozinhas até à interface. Eis como o montar como deve ser, com o SDK modular.

Firestore ou Realtime Database?

O Firebase propõe duas bases. A Realtime Database é a mais antiga: uma grande árvore JSON e consultas limitadas. A Cloud Firestore organiza os dados em coleções de documentos, oferece verdadeiras consultas compostas e índices. Para um projeto novo, escolhe o Firestore, é o que este artigo faz.

Instalar

bash
npm install firebase vuefire

O VueFire é a camada oficial que liga o Firebase à reatividade do Vue. Não é obrigatória, mas evita escrever à mão as subscrições e a respetiva limpeza.

Inicializar

Cria o projeto na consola do Firebase, acrescenta uma aplicação web e copia a configuração. Guarda-a num ficheiro dedicado:

src/firebase/index.js
import { initializeApp } from 'firebase/app'
import { getFirestore } from 'firebase/firestore'

export const firebaseApp = initializeApp({
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
  authDomain: 'mon-projet.firebaseapp.com',
  projectId: 'mon-projet'
})

export const db = getFirestore(firebaseApp)
A chave de API do Firebase é pública

Ao contrário do que o nome sugere, não é um segredo: vai no bundle JavaScript e qualquer pessoa a pode ler. Identifica o projeto, não o autoriza. A segurança assenta inteiramente nas regras do Firestore e na autenticação. Escrever as regras é o verdadeiro trabalho de segurança, não esconder esta chave.

Liga depois o VueFire à aplicação:

src/main.js
import { createApp } from 'vue'
import { VueFire } from 'vuefire'
import App from './App.vue'
import { firebaseApp } from './firebase'

createApp(App)
  .use(VueFire, { firebaseApp })
  .mount('#app')

Ler uma coleção

O useCollection devolve uma referência reativa, preenchida e depois mantida atualizada pelo Firestore:

src/components/ListeAvis.vue
<script setup>
import { collection } from 'firebase/firestore'
import { useCollection } from 'vuefire'
import { db } from '@/firebase'

const avis = useCollection(collection(db, 'avis'))
</script>

<template>
  <ul>
    <li v-for="a in avis" :key="a.id">{{ a.nom }} : {{ a.note }}/5</li>
  </ul>
</template>

É tudo: o ciclo v-for mostra a coleção como qualquer array. Abre a consola do Firebase e altera um documento: a lista muda no navegador sem recarregar. O VueFire fecha a subscrição quando o componente é desmontado, o que evita a fuga clássica dos listeners esquecidos.

Para um documento isolado, o useDocument segue a mesma lógica:

javascript
import { doc } from 'firebase/firestore'
import { useDocument } from 'vuefire'

const produit = useDocument(doc(db, 'produits', '2234'))

Filtrar e ordenar

As consultas compõem-se com query, where, orderBy e limit:

javascript
import { collection, limit, orderBy, query, where } from 'firebase/firestore'

const meilleursAvis = useCollection(
  query(
    collection(db, 'avis'),
    where('note', '>=', 4),
    orderBy('note', 'desc'),
    limit(10)
  )
)

Uma consulta que combina um filtro e uma ordenação em campos diferentes exige um índice composto. O Firestore recusa a consulta e mostra na consola uma ligação que cria o índice num clique, é a mensagem de erro mais útil do produto.

Escrever

As escritas passam pelas funções do SDK, não pelo VueFire. Liga-as a um formulário para criar documentos a partir da interface:

src/components/FormulaireAvis.vue
import {
  addDoc, collection, deleteDoc, doc, serverTimestamp, setDoc, updateDoc
} from 'firebase/firestore'
import { db } from '@/firebase'

// identificador gerado pelo Firestore
await addDoc(collection(db, 'avis'), {
  nom: 'Damien',
  note: 5,
  creeLe: serverTimestamp()
})

// identificador escolhido (cria ou substitui)
await setDoc(doc(db, 'avis', 'avis-2234'), { nom: 'Damien', note: 5 })

// alterar alguns campos
await updateDoc(doc(db, 'avis', 'avis-2234'), { note: 4 })

// apagar
await deleteDoc(doc(db, 'avis', 'avis-2234'))

O serverTimestamp() deixa o Firestore datar do lado do servidor: a hora da máquina do visitante não é de confiança.

Desenvolver contra o emulador

Trabalhar diretamente no projeto de produção estraga os dados e consome a quota. A suite de emuladores do Firebase põe o Firestore a correr localmente.

bash
npm install -D firebase-tools
npx firebase init emulators     # assinalar o Firestore
npx firebase emulators:start --only firestore --project demo-boutique

Um identificador de projeto começado por demo- diz ao emulador para funcionar sem conta nem faturação.

Do lado da aplicação, liga o SDK ao emulador apenas em desenvolvimento:

src/firebase/index.js
import { connectFirestoreEmulator, getFirestore } from 'firebase/firestore'

export const db = getFirestore(firebaseApp)

if (import.meta.env.DEV) {
  connectFirestoreEmulator(db, '127.0.0.1', 8080)
}
O emulador exige Java 21

Desde o firebase-tools 15, uma versão anterior do JDK faz falhar o arranque com a mensagem «firebase-tools no longer supports Java version before 21». Confirma com java -version.

As regras de segurança

Sem regras, a base está fechada ou aberta a toda a gente. O modo «teste» proposto na criação expira ao fim de trinta dias, não o deixes em produção.

firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    match /avis/{avisId} {
      // leitura pública
      allow read: if true;

      // escrita reservada a quem tem sessão iniciada, sobre a sua própria avaliação
      allow create: if request.auth != null
                    && request.resource.data.auteurId == request.auth.uid;
      allow update, delete: if request.auth != null
                    && resource.data.auteurId == request.auth.uid;
    }
  }
}

As regras também se escrevem e testam contra o emulador, o que evita descobrir um buraco em produção.

O que mudou desde o SDK 8

Muitos exemplos ainda em linha usam a antiga API por namespaces. Deixou de funcionar assim desde o Firebase 9:

javascript
// SDK antigo (8 e anteriores) — já não funciona
import firebase from 'firebase'
const db = firebase.initializeApp(config).database()
db.ref('avis').push({ nom: 'Damien' })

// SDK modular (9 e seguintes)
import { initializeApp } from 'firebase/app'
import { addDoc, collection, getFirestore } from 'firebase/firestore'
const db = getFirestore(initializeApp(config))
await addDoc(collection(db, 'avis'), { nom: 'Damien' })

O corte em funções importadas não é cosmético: permite ao bundler guardar apenas o que usas. Numa aplicação que se limita a ler o Firestore, a diferença de peso é evidente.

Do lado do VueFire, a antiga opção firebase: { … } nos componentes e o objeto $firebaseRefs desapareceram: tudo passa por useCollection e useDocument.

Para ir mais longe

Esta montagem dá por adquiridos os componentes e a reatividade do Vue. Se não for o caso, começa pelo tutorial de Vue 3 para principiantes. Para partilhar os dados do Firebase entre componentes distantes, um store Pinia que encapsule as chamadas ao Firestore é um bom padrão.

Erros frequentes

Seguir um tutorial escrito para o SDK 8 firebase.initializeApp(config).database() e o objeto global firebase deixaram de funcionar na versão 9. Tudo passa por funções importadas.
Usar a antiga API do VueFire A opção firebase: { … } nos componentes e o $firebaseRefs desapareceram: usa o useCollection e o useDocument.
Julgar que a chave de API é um segredo Vai no bundle e não autoriza nada por si só. A segurança assenta nas regras do Firestore.
Deixar as regras em modo de teste Expiram ao fim de trinta dias e, até lá, deixam a base aberta à escrita.
Emulador e versão do Java Desde o firebase-tools 15, um JDK anterior à versão 21 faz falhar o arranque.

FirebaseJavaScriptVue.js

Damien Flandrin Programador web desde 2010, criador da Gekkode e do Email Impact. Cada artigo é testado num projeto real antes de ser publicado. Contacto
Newsletter

Os novos testes, tutoriais e projetos, por e-mail.

Testes reproduzíveis, código versionado, resultados datados. Nunca spam.