Com o Cloud Functions, você pode gerenciar 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 snapshot de dados ou do Admin SDK.
Em um ciclo de vida típico, uma função do Firebase Realtime Database faz o seguinte:
- Aguarda alterações em um caminho específico do Realtime Database.
- É acionado quando um evento ocorre e executa suas tarefas.
- Recebe um objeto de dados que contém um instantâneo dos dados armazenados nesse caminho.
Você pode acionar uma função em resposta à gravação, criação, atualização ou exclusão de nós de banco de dados no Firebase Realtime Database. Para controlar quando a função é acionada, especifique um dos manipuladores de eventos e o caminho do Realtime Database onde ele escutará os eventos.
Definir o local da função
A distância entre o local de uma instância do Realtime Database e o local da função pode criar uma latência de rede significativa. Além disso, uma incompatibilidade entre regiões pode resultar em falha na implantação. Para evitar essas situações, especifique o local da função para que corresponda ao local da instância do banco de dados .
Tratamento de eventos do Realtime Database
As funções permitem lidar com eventos do Realtime Database em dois níveis de especificidade; você pode ouvir especificamente apenas eventos de gravação, criação, atualização ou exclusão, ou pode ouvir qualquer alteração de qualquer tipo em uma referência.
Estes manipuladores para responder a eventos do Realtime Database estão disponíveis:
-
onValueWritten()
Acionado quando os dados são criados, atualizados ou excluídos 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.
-
on_value_written()
Acionado quando os dados são criados, atualizados ou excluídos no Realtime Database. -
on_value_created()
Acionado apenas quando os dados são criados no Realtime Database. -
on_value_updated()
Acionado apenas quando os dados são atualizados no Realtime Database. -
on_value_deleted()
Acionado apenas quando os dados são excluídos no Realtime Database.
Importe os módulos necessários
Na sua fonte de função, você deve importar os módulos SDK que deseja usar. Para este exemplo, é necessário importar os módulos HTTP e Realtime Database junto com o módulo Firebase Admin SDK para gravar no Realtime Database.
// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onRequest} = require("firebase-functions/v2/https");
const {onValueCreated} = require("firebase-functions/v2/database");
const {logger} = require("firebase-functions");
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp();
# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import db_fn, https_fn
# The Firebase Admin SDK to access the Firebase Realtime Database.
from firebase_admin import initialize_app, db
app = initialize_app()
Especifique a instância e o caminho
Para controlar quando e onde sua função deve ser acionada, configure-a com um caminho e, opcionalmente, uma instância do Realtime Database. Se você não especificar uma instância, a função escutará todas as instâncias do Realtime Database na região da função. Você também pode 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:
// 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) => {
// …
}
);
# 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.
@db_fn.on_value_written(r"/user/{uid}")
def onwrittenfunctiondefault(event: db_fn.Event[db_fn.Change]):
# ...
pass
# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
@db_fn.on_value_written(
reference=r"/user/{uid}",
instance="my-app-db-2",
# This example assumes us-central1, but to set location:
# region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
# ...
pass
# 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.
@db_fn.on_value_written(
reference=r"/user/{uid=*@gmail.com}",
instance="my-app-db-*",
# This example assumes us-central1, but to set location:
# region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
# ...
pass
Esses parâmetros direcionam sua função para lidar com gravações em um determinado caminho na instância do Realtime Database.
As especificações de caminho correspondem a todas as gravações que tocam um caminho, incluindo gravações que acontecem 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 usar diversas funções em caminhos mais profundos, em vez de uma única função próxima à raiz do seu banco de dados. Para obter o melhor desempenho, solicite dados apenas no nível mais profundo possível.
Curinga e captura
Você pode usar {key}
, {key=*}
, {key=prefix*}
, {key=*suffix}
para captura. *
, prefix*
, *suffix
para caracteres curinga de segmento único. Observação: **
representa caracteres curinga de vários segmentos, que o Realtime Database não suporta. Consulte Compreender os padrões de caminho .
Curinga de caminho. Você pode especificar um componente de caminho como curinga:
- Usando asterisco,
*
. Por exemplo,foo/*
corresponde a qualquer filho um nível da hierarquia de nós abaixo defoo/
. - Usando um segmento contendo exatamente asterisco,
*
. Por exemplo,foo/app*-us
corresponde a qualquer segmento filho abaixo defoo/
com prefixo deapp
e sufixo-us
.
Caminhos com curingas podem corresponder a vários eventos, por exemplo, de uma única gravação. Uma inserção de
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
corresponde ao caminho "/foo/*"
duas vezes: uma vez com "hello": "world"
e novamente com "firebase": "functions"
.
Captura de caminho. Você pode capturar correspondências de caminho em variáveis nomeadas para serem usadas em 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.
Curinga de instância. Você também pode especificar um componente de instância usando caracteres curinga. Um curinga de instância pode ter prefixo, sufixo ou ambos (por exemplo my-app-*-prod
).
Curinga e referência de captura
Com o Cloud Functions (2ª geração) e o Realtime Database, um padrão pode ser usado ao especificar ref
e instance
. Cada interface de gatilho terá as seguintes opções para definir o escopo de uma função:
ref | Especificando instance | Comportamento |
---|---|---|
Único ( /foo/bar ) | Não especificando | Manipulador de escopos para todas as instâncias na região de função. |
Único ( /foo/bar ) | Único ( 'my-new-db' ) | Manipulador de escopos para a instância específica na região de função. |
Único ( /foo/bar ) | Padrão ( 'inst-prefix*' ) | Manipulador de escopos para todas as instâncias que correspondem ao padrão na região de função. |
Padrão ( /foo/{bar} ) | Não especificando | Manipulador de escopos para todas as instâncias na região de função. |
Padrão ( /foo/{bar} ) | Único ( 'my-new-db' ) | Manipulador de escopos para a instância específica na região de função. |
Padrão ( /foo/{bar} ) | Padrão ( 'inst-prefix*' ) | Manipulador de escopos para todas as instâncias que correspondem ao padrão na região de função. |
Lidar com dados de eventos
Quando um evento do Realtime Database é acionado, ele passa um objeto Event
para sua função de manipulador. Este objeto possui uma propriedade data
, que, para eventos de criação e exclusão, contém um instantâneo dos dados criados ou excluídos.
Neste exemplo, a função recupera os dados do caminho referenciado, converte a string naquele local em 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
// for all databases in 'us-central1'
exports.makeuppercase = onValueCreated(
"/messages/{pushId}/original",
(event) => {
// Grab the current value of what was written to the Realtime Database.
const original = event.data.val();
logger.log("Uppercasing", event.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing
// asynchronous tasks inside a function, 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);
},
);
@db_fn.on_value_created(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[Any]) -> None:
"""Listens for new messages added to /messages/{pushId}/original and
creates an uppercase version of the message to /messages/{pushId}/uppercase
"""
# Grab the value that was written to the Realtime Database.
original = event.data
if not isinstance(original, str):
print(f"Not a string: {event.reference}")
return
# Use the Admin SDK to set an "uppercase" sibling.
print(f"Uppercasing {event.params['pushId']}: {original}")
upper = original.upper()
parent = db.reference(event.reference).parent
if parent is None:
print("Message can't be root node.")
return
parent.child("uppercase").set(upper)
Lendo o valor anterior
Para eventos write
ou update
, a propriedade data
é um objeto Change
que contém dois instantâneos que representam o estado dos dados antes e depois do evento acionador. O objeto Change
tem uma propriedade before
que permite inspecionar o que foi salvo no Realtime Database antes do evento e uma propriedade after
que representa o estado dos dados após a ocorrência do evento.
Por exemplo, a propriedade before
pode ser usada para garantir que a função apenas coloque texto em maiúscula quando for criada 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);
});
@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase2(event: db_fn.Event[db_fn.Change]) -> None:
"""Listens for new messages added to /messages/{pushId}/original and
creates an uppercase version of the message to /messages/{pushId}/uppercase
"""
# Only edit data when it is first created.
if event.data.before is not None:
return
# Exit when the data is deleted.
if event.data.after is None:
return
# Grab the value that was written to the Realtime Database.
original = event.data.after
if not hasattr(original, "upper"):
print(f"Not a string: {event.reference}")
return
# Use the Admin SDK to set an "uppercase" sibling.
print(f"Uppercasing {event.params['pushId']}: {original}")
upper = original.upper()
parent = db.reference(event.reference).parent
if parent is None:
print("Message can't be root node.")
return
parent.child("uppercase").set(upper)