Gatilhos do Realtime Database

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

  1. Espera por mudanças em um caminho específico do Realtime Database.
  2. É acionada quando um evento ocorre e realiza as tarefas dela.
  3. Recebe um objeto de dados que contém um snapshot dos dados armazenados naquele caminho.

É possível acionar uma função em resposta à gravação, criação, atualização ou exclusão de nós do banco de dados no Firebase Realtime Database.

Acionar uma função quando houver mudanças no Firebase Realtime Database

Use o subpacote firebase-functions/v2/database para criar uma função que gerencie eventos do Firebase Realtime Database. Para controlar quando a função é acionada, especifique um dos manipuladores de eventos e o caminho do Realtime Database em que ela vai detectar os eventos.

Como definir o local da função

A distância entre o local da instância do Realtime Database e o da função pode criar uma latência de rede significativa. Além disso, uma incompatibilidade entre as regiões pode resultar em uma falha na implantação. Para evitar essas situações, especifique o local da função para que ela corresponda ao local da instância do banco de dados.

Processar eventos do Realtime Database

O Functions processa os eventos do Realtime Database em dois níveis de especificidade. É possível detectar especificamente apenas eventos de gravação, criação, atualização ou exclusão, ou detectar alterações de qualquer tipo em uma referência.

Estes gerenciadores de resposta a eventos do Realtime Database estão disponíveis:

  • onValueWritten(): acionado apenas quando os dados são gravados no Realtime Database.
  • onValueCreated(): acionado apenas quando os dados são criados no Realtime Database.
  • onValueUpdated(): acionado apenas quando os dados são atualizados no Realtime Database.
  • onValueDeleted(): acionado apenas quando os dados são excluídos no Realtime Database.

Especificar a instância e o caminho

Para controlar quando e onde sua função deve ser acionada, faça a configuração com um caminho e, se preferir, uma instância do Realtime Database. Se você não especificar uma instância, a função será implantada em todas as instâncias do Realtime Database na região da função. Também é possível especificar um padrão de instância do Realtime Database para implantar em um subconjunto seletivo de instâncias na mesma região.

Por exemplo, usando onValueWritten():

# All Realtime Database instances in default function region us-central1 at path "/user/{uid}"
# There must be at least one Realtime Database present in us-central1.
const onwrittenfunctiondefault = onValueWritten("/user/{uid}", (event) => {
  // …
});

# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
const onwrittenfunctioninstance = onValueWritten(
  {
    ref: "/user/{uid}",
    instance: "my-app-db-2"
    // This example assumes us-central1, but to set location:
    // region: "europe-west1"
  },
  (event) => {
    // …
  }
);

# Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
# There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
const onwrittenfunctioninstance = onValueWritten(
  {
    ref: "/user/{uid=*@gmail.com}",
    instance: "my-app-db-*"
    // This example assumes us-central1, but to set location:
    // region: "europe-west1"
  },
  (event) => {
    // …
  }
);

Esses parâmetros direcionam sua função para lidar com as gravações em um determinado caminho na instância do Realtime Database.

As especificações de caminho correspondem a todas as gravações realizadas em um caminho, incluindo as que ocorrem em qualquer lugar abaixo dele. Se você definir o caminho para sua função como /foo/bar, ele vai corresponder aos eventos nestes dois 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 usar várias funções em caminhos mais profundos em vez de uma única função próxima à raiz do banco de dados. Para ter o melhor desempenho, solicite apenas dados no nível mais profundo possível.

Caracteres curinga e captura

Use {key}, {key=*}, {key=prefix*}, {key=*suffix} para fazer a captura. *, prefix*, *suffix para caracteres curinga em segmento único. Observação: ** representa caracteres curinga com vários segmentos, que não são compatíveis com RTDB. Consulte Entender os padrões de caminho.

Caracteres curinga de caminho. É possível especificar um componente de caminho como caractere curinga:

  • Usando asterisco, *. Por exemplo, foo/* corresponde a qualquer nível filho da hierarquia de nós abaixo de foo/.
  • Usando um segmento que contenha exatamente o asterisco, *. Por exemplo: foo/app*-us corresponde a qualquer segmento filho abaixo de foo/ com o prefixo app e o sufixo -us.

Os caminhos com caracteres curinga podem corresponder a vários eventos, como uma única gravação. Uma inserção de

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

corresponde ao caminho "/foo/*" duas vezes: em "hello": "world" e depois em "firebase": "functions".

Captura de caminho. É possível capturar correspondências de caminho em variáveis nomeadas que serão usadas no seu código de função (por exemplo, /user/{uid}, /user/{uid=*-us}).

Os valores das variáveis de captura estão disponíveis no objeto database.DatabaseEvent.params da sua função.

Caracteres curinga de instância. Também é possível especificar um componente de instância usando caracteres curinga. Um caractere curinga de instância pode ter prefixo, sufixo ou ambos (por exemplo, my-app-*-prod).

Referência de caracteres curinga e de captura.

É possível usar um padrão ao especificar ref e instance com o Cloud Functions (2nd gen) e o Realtime Database. Cada interface de gatilho terá as opções a seguir de escopo de uma função:

Como especificar ref Como especificar instance Comportamento
Única (/foo/bar) Não especificado Gerenciador de escopos para todas as instâncias na região da função.
Única (/foo/bar) Única (‘my-new-db') Gerenciador de escopos da instância específica na região da função.
Única (/foo/bar) Padrão (‘inst-prefix*') Gerenciador de escopos para todas as instâncias que correspondem ao padrão na região da função.
Padrão (/foo/{bar}) Não especificado Gerenciador de escopos para todas as instâncias na região da função.
Padrão (/foo/{bar}) Única (‘my-new-db') Gerenciador de escopos da instância específica na região da função.
Padrão (/foo/{bar}) Padrão (‘inst-prefix*') Gerenciador de escopos para todas as instâncias que correspondem ao padrão na região da função.

Processar dados de eventos

Ao processar um evento do Realtime Database, o objeto de dados retornado é um DataSnapshot.

Para eventos onValueWritten ou onValueUpdated, o primeiro parâmetro é um Change objeto que contém dois snapshots que representam o estado dos dados antes e depois do evento de acionamento.

Para eventos onValueCreated e onValueDeleted, o objeto de dados retornado é um snapshot dos dados criados ou excluídos.

Neste exemplo, a função recupera o snapshot do caminho especificado foo/bar como snap, converte a string nessa localização para maiúscula e grava a 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
export makeuppercase = onValueCreated("foo/bar", (event) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = event.data.val();
      functions.logger.log('Uppercasing', event.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 event.data.ref.parent.child('uppercase').set(uppercase);
    });

Como ler 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 em que todos os métodos (por exemplo, val() e exists()) se referem ao valor anterior. Para ler o novo valor mais uma vez, use o original DataSnapshot ou leia a propriedade after. Essa propriedade em qualquer Change será 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 só transcreva texto em caixa alta quando ele é criado pela primeira vez:

    exports makeuppercase = onValueWritten("/messages/{pushId}/original", (event) => {
          // Only edit data when it is first created.
          if (event.data.before.exists()) {
            return null;
          }
          // Exit when the data is deleted.
          if (!event.data.after.exists()) {
            return null;
          }
          // Grab the current value of what was written to the Realtime Database.
          const original = event.data.after.val();
          console.log('Uppercasing', event.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 event.data.after.ref.parent.child('uppercase').set(uppercase);
        });