Ir para o console

Estender o Cloud Firestore com o Cloud Functions

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

Consulte exemplos de casos de uso em O que posso fazer com o Cloud Functions? ou no repositório do GitHub de Amostras de funções.

Acionar uma função do Cloud Firestore

O SDK do Cloud Functions para Firebase exporta um objeto functions.firestore que permite que você crie gerenciadores relacionados a eventos específicos.

O Cloud Firestore é compatível com os eventos create, update, delete e write:

Tipo de evento Acionador
onCreate Acionado quando um documento é gravado pela primeira vez.
onUpdate Acionado 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 ativou um projeto para o Cloud Functions para Firebase, leia Primeiros passos: gravar e implantar suas primeiras funções para configurar um projeto.

Acionar uma função quando um documento específico é alterado

Se você quiser ativar um evento para qualquer alteração em um documento específico, use 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
    });

Acionar uma função quando um novo documento é criado

Você poderá ativar o acionamento de uma função sempre que um novo documento for criado em uma coleção. Para isso, use um gerenciador onCreate() com um caractere 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 ...
    });

Acionar uma função quando um documento é atualizado

Você também poderá ativar o acionamento de uma função quando um documento for atualizado. Para isso, use a função onUpdate() com um caractere curinga. Esta função de exemplo chamará updateUser se um usuário alterar o perfil dele:

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 ...
    });

Acionar uma função quando um documento é excluído

Você também poderá ativar uma função quando um documento for excluído. Para isso, use a função onDelete() com um caractere curinga. Esta função de exemplo chama deleteUser quando um usuário exclui o perfil de usuário dele:

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 ...
    });

Acionar uma função para qualquer alteração em um documento

Se o tipo de evento acionado não for relevante, você poderá identificar todas as alterações em um documento do Cloud Firestore. Para isso, use a função onWrite() com um caractere curinga. Esta função de exemplo chamará 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 ...
    });

Processar dados do evento

Leitura de dados

Quando uma função é acionada, você pode ver os dados de um documento que foi atualizado ou coletá-los antes da atualização. Veja 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.updateUser = 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 as propriedades como faria em qualquer outro objeto. Se preferir, use 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');

Gravação de dados

Cada invocação de função está associada a um documento específico no seu banco de dados do Cloud Firestore. É possível acessar esse documento como um DocumentReference na propriedade ref do instantâneo retornado pela sua função.

Esse DocumentReference é originário do SDK para 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;

      // 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});
    });

Uso de caracteres curingas e parâmetros

Se você não sabe a qual documento atribuir um acionador de evento, é possível usar um {wildcard} no lugar do código do documento.

Node.js

// Listen for changes in all documents and all sub-collections
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 de qualquer documento em users for alterado, ele corresponderá a um caractere curinga chamado userId.

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

As correspondências de caractere curinga são extraídas do caminho do documento e armazenadas em event.params. É possível definir quantos caracteres curinga você quiser para substituir a coleção explícita ou os códigos do documento.

Node.js

// Listen for changes in all documents 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"}
    });

Limitações e garantias

Ao desenvolver seus aplicativos, lembre-se de que tanto o Cloud Firestore quanto o Cloud Functions para Firebase estão em versão Beta, o que pode resultar em comportamentos inesperados.

Algumas limitações conhecidas incluem:

  • O acionamento de uma função pode levar mais de cinco segundos após uma alteração nos dados do Cloud Firestore.
  • No momento, o envio de invocações de função não é garantido. À medida que a integração do Cloud Firestore e do Cloud Functions é aprimorada, planejamos garantir pelo menos um envio. No entanto, isso pode não ocorrer na versão Beta. Isso também pode resultar em múltiplas invocações para um único evento. Portanto, para garantir funções de melhor qualidade, grave-as usando o método de idempotência.
  • Não garantimos acionamentos em ordem. Alterações rápidas podem acionar invocações de função em uma ordem inesperada.