Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Acionadores do Realtime Database

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

Com o Cloud Functions, você pode lidar com eventos no Firebase Realtime Database sem precisar atualizar o código do cliente. O Cloud Functions permite executar operações do Realtime Database com privilégios administrativos totais e garante que cada alteração no Realtime Database seja processada individualmente. Você pode fazer alterações no Firebase Realtime Database por meio do DataSnapshot ou do Admin SDK .

Em um ciclo de vida típico, uma função do Firebase Realtime Database faz o seguinte:

  1. Aguarda alterações em um local específico do Realtime Database.
  2. Aciona quando um evento ocorre e executa suas tarefas (consulte O que posso fazer com o Cloud Functions? para obter exemplos de casos de uso).
  3. Recebe um objeto de dados que contém um instantâneo dos dados armazenados no documento especificado.

Acionar uma função do Realtime Database

Crie novas funções para eventos do Realtime Database com functions.database . Para controlar quando a função é acionada, especifique um dos manipuladores de eventos e especifique o caminho do Realtime Database onde ele escutará os eventos.

Definir o manipulador de eventos

As funções permitem lidar com eventos do Realtime Database em dois níveis de especificidade; você pode ouvir especificamente apenas eventos de criação, atualização ou exclusão, ou pode ouvir qualquer alteração de qualquer tipo em um caminho. O Cloud Functions oferece suporte a estes manipuladores de eventos para o Realtime Database:

  • onWrite() , que é acionado quando os dados são criados, atualizados ou excluídos no Realtime Database.
  • onCreate() , que é acionado quando novos dados são criados no Realtime Database.
  • onUpdate() , que é acionado quando os dados são atualizados no Realtime Database .
  • onDelete() , que é acionado quando os dados são excluídos do Realtime Database .

Especifique a instância e o caminho

Para controlar quando e onde sua função deve ser acionada, chame ref(path) para especificar um caminho e, opcionalmente, especifique uma instância do Realtime Database com instance('INSTANCE_NAME') . Se você não especificar uma instância, a função será implantada na instância padrão do Realtime Database para o projeto Firebase Por exemplo:

  • Instância padrão do Realtime Database: functions.database.ref('/foo/bar')
  • Instância chamada "my-app-db-2": functions.database.instance('my-app-db-2').ref('/foo/bar')

Esses métodos direcionam sua função para lidar com gravações em um determinado caminho dentro da instância do Realtime Database. As especificações de caminho correspondem a todas as gravações que tocam um caminho, incluindo gravações que ocorrem em qualquer lugar abaixo dele. Se você definir o caminho para sua função como /foo/bar , ele corresponderá aos eventos em ambos os locais:

 /foo/bar
 /foo/bar/baz/really/deep/path

Em ambos os casos, o Firebase interpreta que o evento ocorre em /foo/bar , e os dados do evento incluem os dados antigos e novos em /foo/bar . Se os dados do evento forem grandes, considere o uso de várias funções em caminhos mais profundos em vez de uma única função próxima à raiz de seu banco de dados. Para obter o melhor desempenho, solicite apenas dados no nível mais profundo possível.

Você pode especificar um componente de caminho como um curinga colocando-o entre chaves; ref('foo/{bar}') corresponde a qualquer filho de /foo . Os valores desses componentes de caminho curinga estão disponíveis no objeto EventContext.params de sua função. Neste exemplo, o valor está disponível como context.params.bar .

Caminhos com curingas podem corresponder a vários eventos de uma única gravação. Uma inserção de

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

corresponde ao caminho "/foo/{bar}" duas vezes: uma vez com "hello": "world" e novamente com "firebase": "functions" .

Processar dados de evento

Ao manipular um evento do Realtime Database, o objeto de dados retornado é um DataSnapshot . Para eventos onWrite ou onUpdate , o primeiro parâmetro é um objeto Change que contém dois instantâneos que representam o estado dos dados antes e depois do evento acionador. Para eventos onCreate e onDelete , o objeto de dados retornado é um instantâneo dos dados criados ou excluídos.

Neste exemplo, a função recupera o instantâneo para o caminho especificado como snap , converte a string nesse local para letras maiúsculas e grava essa string modificada no banco de dados:

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snapshot, context) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = snapshot.val();
      functions.logger.log('Uppercasing', context.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing asynchronous tasks inside a Functions such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
      return snapshot.ref.parent.child('uppercase').set(uppercase);
    });

Acessando informações de autenticação do usuário

Em EventContext.auth e EventContext.authType , você pode acessar as informações do usuário, incluindo permissões, para o usuário que acionou uma função. Isso pode ser útil para impor regras de segurança, permitindo que sua função conclua diferentes operações com base no nível de permissões do usuário:

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

exports.simpleDbFunction = functions.database.ref('/path')
    .onCreate((snap, context) => {
      if (context.authType === 'ADMIN') {
        // do something
      } else if (context.authType === 'USER') {
        console.log(snap.val(), 'written by', context.auth.uid);
      }
    });

Além disso, você pode aproveitar as informações de autenticação do usuário para "representar" um usuário e executar operações de gravação em nome do usuário. Certifique-se de excluir a instância do aplicativo conforme mostrado abaixo para evitar problemas de simultaneidade:

exports.impersonateMakeUpperCase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snap, context) => {
      const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
      appOptions.databaseAuthVariableOverride = context.auth;
      const app = admin.initializeApp(appOptions, 'app');
      const uppercase = snap.val().toUpperCase();
      const ref = snap.ref.parent.child('uppercase');

      const deleteApp = () => app.delete().catch(() => null);

      return app.database().ref(ref).set(uppercase).then(res => {
        // Deleting the app is necessary for preventing concurrency leaks
        return deleteApp().then(() => res);
      }).catch(err => {
        return deleteApp().then(() => Promise.reject(err));
      });
    });

Lendo o valor anterior

O objeto Change tem uma propriedade before que permite inspecionar o que foi salvo no Realtime Database antes do evento. A propriedade before retorna um DataSnapshot onde todos os métodos (por exemplo, val() e exists() ) referem-se ao valor anterior. Você pode ler o novo valor novamente usando o DataSnapshot original ou lendo a propriedade after . Esta propriedade em qualquer Change é outro DataSnapshot que representa o estado dos dados depois que o evento aconteceu.

Por exemplo, a propriedade before pode ser usada para garantir que a função apenas coloque o texto em letras maiúsculas quando for criada pela primeira vez:

exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onWrite((change, context) => {
      // Only edit data when it is first created.
      if (change.before.exists()) {
        return null;
      }
      // Exit when the data is deleted.
      if (!change.after.exists()) {
        return null;
      }
      // Grab the current value of what was written to the Realtime Database.
      const original = change.after.val();
      console.log('Uppercasing', context.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing asynchronous tasks inside a Functions such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
      return change.after.ref.parent.child('uppercase').set(uppercase);
    });