Estenda o Cloud Firestore com Cloud Functions

Com o Cloud Functions, você pode implantar o código Node.js para lidar com eventos acionados por alterações no seu banco de dados do Cloud Firestore. Isso permite que você adicione facilmente funcionalidades do lado do servidor ao seu aplicativo sem executar seus próprios servidores.

Para ver exemplos de casos de uso, consulte O que posso fazer com o Cloud Functions? ou o repositório GitHub de exemplos de funções .

Gatilhos de função do Cloud Firestore

O SDK do Cloud Functions para Firebase exporta um objeto functions.firestore que permite criar manipuladores vinculados a eventos específicos do Cloud Firestore.

Tipo de evento Acionar
onCreate Acionado quando um documento é gravado pela primeira vez.
onUpdate Disparado quando um documento já existe e tem algum valor alterado.
onDelete Acionado quando um documento com dados é excluído.
onWrite Acionado quando onCreate , onUpdate ou onDelete é acionado.

Se você ainda não tem um projeto habilitado para Cloud Functions para Firebase, leia Primeiros passos: escreva e implante suas primeiras funções para configurar e configurar seu projeto do Cloud Functions para Firebase.

Escrevendo funções acionadas pelo Cloud Firestore

Defina um gatilho de função

Para definir um gatilho do Cloud Firestore, especifique um caminho de documento e um tipo de evento:

Node.js

const functions = require('firebase-functions');

exports.myFunction = functions.firestore
  .document('my-collection/{docId}')
  .onWrite((change, context) => { /* ... */ });

Os caminhos dos documentos podem fazer referência a um documento específico ou a um padrão curinga .

Especifique um único documento

Se quiser acionar um evento para qualquer alteração em um documento específico, você pode usar a seguinte função.

Node.js

// Listen for any change on document `marie` in collection `users`
exports.myFunctionName = functions.firestore
    .document('users/marie').onWrite((change, context) => {
      // ... Your code here
    });

Especifique um grupo de documentos usando curingas

Se você quiser anexar um gatilho a um grupo de documentos, como qualquer documento de uma determinada coleção, use um {wildcard} no lugar do ID do documento:

Node.js

// Listen for changes in all documents in the 'users' collection
exports.useWildcard = functions.firestore
    .document('users/{userId}')
    .onWrite((change, context) => {
      // If we set `/users/marie` to {name: "Marie"} then
      // context.params.userId == "marie"
      // ... and ...
      // change.after.data() == {name: "Marie"}
    });

Neste exemplo, quando qualquer campo em qualquer documento em users é alterado, ele corresponde a um curinga chamado userId .

Se um documento em users tiver subcoleções e um campo em um dos documentos dessas subcoleções for alterado, o curinga userId não será acionado.

As correspondências de curinga são extraídas do caminho do documento e armazenadas em context.params . Você pode definir quantos curingas desejar para substituir IDs explícitos de coleções ou documentos, por exemplo:

Node.js

// Listen for changes in all documents in the 'users' collection and all subcollections
exports.useMultipleWildcards = functions.firestore
    .document('users/{userId}/{messageCollectionId}/{messageId}')
    .onWrite((change, context) => {
      // If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
      // context.params.userId == "marie";
      // context.params.messageCollectionId == "incoming_messages";
      // context.params.messageId == "134";
      // ... and ...
      // change.after.data() == {body: "Hello"}
    });

Gatilhos de eventos

Acione uma função quando um novo documento for criado

Você pode acionar uma função para disparar sempre que um novo documento for criado em uma coleção usando um manipulador onCreate() com um curinga . Esta função de exemplo chama createUser sempre que um novo perfil de usuário é adicionado:

Node.js

exports.createUser = functions.firestore
    .document('users/{userId}')
    .onCreate((snap, context) => {
      // Get an object representing the document
      // e.g. {'name': 'Marie', 'age': 66}
      const newValue = snap.data();

      // access a particular field as you would any JS property
      const name = newValue.name;

      // perform desired operations ...
    });

Acione uma função quando um documento for atualizado

Você também pode acionar uma função para disparar quando um documento é atualizado usando a função onUpdate() com um curinga . Esta função de exemplo chama updateUser se um usuário alterar seu perfil:

Node.js

exports.updateUser = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Get an object representing the document
      // e.g. {'name': 'Marie', 'age': 66}
      const newValue = change.after.data();

      // ...or the previous value before this update
      const previousValue = change.before.data();

      // access a particular field as you would any JS property
      const name = newValue.name;

      // perform desired operations ...
    });

Acione uma função quando um documento for excluído

Você também pode acionar uma função quando um documento é excluído usando a função onDelete() com um curinga . Este exemplo de função chama deleteUser quando um usuário exclui seu perfil de usuário:

Node.js

exports.deleteUser = functions.firestore
    .document('users/{userID}')
    .onDelete((snap, context) => {
      // Get an object representing the document prior to deletion
      // e.g. {'name': 'Marie', 'age': 66}
      const deletedValue = snap.data();

      // perform desired operations ...
    });

Acione uma função para todas as alterações em um documento

Se você não se importa com o tipo de evento que está sendo disparado, você pode ouvir todas as alterações em um documento do Cloud Firestore usando a função onWrite() com um curinga . Esta função de exemplo chama modifyUser se um usuário for criado, atualizado ou excluído:

Node.js

exports.modifyUser = functions.firestore
    .document('users/{userID}')
    .onWrite((change, context) => {
      // Get an object with the current document value.
      // If the document does not exist, it has been deleted.
      const document = change.after.exists ? change.after.data() : null;

      // Get an object with the previous document value (for update or delete)
      const oldDocument = change.before.data();

      // perform desired operations ...
    });

Lendo e gravando dados

Quando uma função é acionada, ela fornece um instantâneo dos dados relacionados ao evento. Você pode usar esse snapshot para ler ou gravar no documento que acionou o evento ou usar o SDK Admin do Firebase para acessar outras partes do seu banco de dados.

Dados do Evento

Lendo dados

Quando uma função é acionada, você pode querer obter dados de um documento que foi atualizado ou obter os dados antes da atualização. Você pode obter os dados anteriores usando change.before.data() , que contém o instantâneo do documento antes da atualização. Da mesma forma, change.after.data() contém o estado do instantâneo do documento após a atualização.

Node.js

exports.updateUser2 = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Get an object representing the current document
      const newValue = change.after.data();

      // ...or the previous value before this update
      const previousValue = change.before.data();
    });

Você pode acessar propriedades como faria em qualquer outro objeto. Alternativamente, você pode usar a função get para acessar campos específicos:

Node.js

// Fetch data using standard accessors
const age = snap.data().age;
const name = snap.data()['name'];

// Fetch data using built in accessor
const experience = snap.get('experience');

Gravando dados

Cada invocação de função está associada a um documento específico no banco de dados do Cloud Firestore. Você pode acessar esse documento como DocumentReference na propriedade ref do instantâneo retornado para sua função.

Este DocumentReference vem do SDK Node.js do Cloud Firestore e inclui métodos como update() , set() e remove() para que você possa modificar facilmente o documento que acionou a função.

Node.js

// Listen for updates to any `user` document.
exports.countNameChanges = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Retrieve the current and previous value
      const data = change.after.data();
      const previousData = change.before.data();

      // We'll only update if the name has changed.
      // This is crucial to prevent infinite loops.
      if (data.name == previousData.name) {
        return null;
      }

      // Retrieve the current count of name changes
      let count = data.name_change_count;
      if (!count) {
        count = 0;
      }

      // Then return a promise of a set operation to update the count
      return change.after.ref.set({
        name_change_count: count + 1
      }, {merge: true});
    });

Dados fora do evento acionador

O Cloud Functions é executado em um ambiente confiável, o que significa que são autorizados como uma conta de serviço no seu projeto. Você pode realizar leituras e gravações usando o SDK Admin do Firebase :

Node.js

const admin = require('firebase-admin');
admin.initializeApp();

const db = admin.firestore();

exports.writeToFirestore = functions.firestore
  .document('some/doc')
  .onWrite((change, context) => {
    db.doc('some/otherdoc').set({ ... });
  });

Limitações

Observe as seguintes limitações para gatilhos do Cloud Firestore para Cloud Functions:

  • O pedido não é garantido. Mudanças rápidas podem acionar invocações de funções em uma ordem inesperada.
  • Os eventos são entregues pelo menos uma vez, mas um único evento pode resultar em múltiplas invocações de funções. Evite depender da mecânica exatamente uma vez e escreva funções idempotentes .
  • O Cloud Firestore no modo Datastore requer Cloud Functions (2ª geração). O Cloud Functions (1ª geração) não é compatível com o modo Datastore.
  • O Cloud Functions (1ª geração) funciona apenas com banco de dados "(padrão)" e não é compatível com bancos de dados nomeados do Cloud Firestore. Use o Cloud Functions (2ª geração) para configurar eventos para bancos de dados nomeados.
  • Um gatilho está associado a um único banco de dados. Você não pode criar um gatilho que corresponda a vários bancos de dados.
  • A exclusão de um banco de dados não exclui automaticamente nenhum acionador desse banco de dados. O gatilho para de entregar eventos, mas continua existindo até que você exclua o gatilho .
  • Se um evento correspondente exceder o tamanho máximo da solicitação , o evento poderá não ser entregue ao Cloud Functions (1ª geração).
    • Os eventos não entregues devido ao tamanho da solicitação são registrados nos logs da plataforma e contam para o uso do log do projeto.
    • Você pode encontrar esses logs no Logs Explorer com a mensagem "O evento não pode ser entregue à função Cloud devido ao tamanho exceder o limite para a 1ª geração..." de gravidade do error . Você pode encontrar o nome da função no campo functionName . Se o campo receiveTimestamp ainda estiver dentro de uma hora, você poderá inferir o conteúdo real do evento lendo o documento em questão com um instantâneo antes e depois do carimbo de data/hora.
    • Para evitar essa cadência, você pode:
      • Migrar e fazer upgrade para o Cloud Functions (2ª geração)
      • Reduza o tamanho do documento
      • Exclua o Cloud Functions em questão
    • Você pode desativar o registro em log usando exclusões , mas observe que os eventos ofensivos ainda não serão entregues.