Con Cloud Functions, puedes controlar eventos en Firebase Realtime Database sin necesidad de actualizar el código del cliente. Cloud Functions te permite ejecutar operaciones de Realtime Database con privilegios administrativos completos y garantiza que cada cambio en Realtime Database se procese de forma individual. Puedes realizar cambios en Firebase Realtime Database a través de la instantánea de datos o el SDK de Admin.
En un ciclo de vida típico, una función de Firebase Realtime Database hace lo siguiente:
- Espera a que ocurran cambios en una ruta de Realtime Database en particular.
- Se activa cuando ocurre un evento y realiza sus tareas.
- Recibe un objeto de datos que contiene una instantánea de los datos almacenados en esa ruta.
Puedes activar una función en respuesta a la escritura, creación, actualización o eliminación de nodos de la base de datos en Firebase Realtime Database. Para controlar cuándo se debe activar la función, especifica uno de los controladores de eventos y la ruta de acceso de Realtime Database en la que se detectarán los eventos.
Configura la ubicación de la función
La distancia entre la ubicación de la instancia de Realtime Database y la ubicación de la función puede generar una latencia de red considerable. Además, la discrepancia entre las regiones puede provocar un error de implementación. Para evitar estas situaciones, especifica la ubicación de la función para que coincida con la ubicación de la instancia de la base de datos.
Controla eventos de Realtime Database
Las funciones te permiten controlar los eventos de Realtime Database en dos niveles de especificidad: puedes detectar específicamente solo eventos de escritura, creación, actualización o eliminación, o puedes detectar cambios de cualquier tipo en cualquier referencia.
Estos son los controladores disponibles para responder a eventos de Realtime Database:
Node.js
onValueWritten()
Se activa cuando se crean, actualizan o borran datos en Realtime Database.onValueCreated()
Solo se activa cuando se crean datos en Realtime Database.onValueUpdated()
Solo se activa cuando se actualizan datos en Realtime Database.onValueDeleted()
Solo se activa cuando se borran datos en Realtime Database.
Python
on_value_written()
Se activa cuando se crean, actualizan o borran datos en Realtime Database.on_value_created()
Solo se activa cuando se crean datos en Realtime Database.on_value_updated()
Solo se activa cuando se actualizan datos en Realtime Database.on_value_deleted()
Solo se activa cuando se borran datos en Realtime Database.
Importa módulos obligatorios
En la fuente de la función, debes importar los módulos del SDK que quieres usar. En este ejemplo, es necesario importar los módulos HTTP y Realtime Database, junto con el módulo Firebase Admin SDK para escribir en Realtime Database.
Node.js
// 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();
Python
# 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()
Especifica la instancia y la ruta
Para controlar cuándo y dónde se debe activar la función, configúrala con una ruta de acceso y, opcionalmente, una instancia de Realtime Database. Si no especificas una instancia, la función escucha en todas las instancias de Realtime Database de la región de la función. También puedes especificar un patrón de instancias de Realtime Database para implementar en un subconjunto selectivo de instancias en la misma región.
Por ejemplo:
Node.js
// 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) => { // … } );
Python
# 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
Estos parámetros le indican a la función que realice las operaciones de escritura en una ruta de acceso específica dentro de la instancia de Realtime Database.
Las coincidencias de las especificaciones de la ruta de acceso se establecen con todas las escrituras que
afectan una ruta, incluidas las que ocurren por debajo de esa ruta. Si configuras la ruta
para la función como /foo/bar
, se establecerán coincidencias con los eventos que ocurren en estas dos ubicaciones:
/foo/bar
/foo/bar/baz/really/deep/path
En ambos casos, Firebase interpreta que el evento ocurre en /foo/bar
,
y los datos del evento incluyen los datos antiguos y nuevos en /foo/bar
. Si existe la posibilidad de que los eventos
generen un gran volumen de datos, es recomendable que uses varias funciones en rutas de acceso más profundas en lugar de
una sola función cerca de la raíz de la base de datos. Para obtener el mejor rendimiento,
solicita datos únicamente en el nivel más profundo posible.
Comodín y captura
Puedes usar {key}
, {key=*}
, {key=prefix*}
y {key=*suffix}
para la captura. y *
, prefix*
, *suffix
para el comodín de un solo segmento.
Nota: **
representa comodines de múltiples segmentos, lo que no admite Realtime Database.
Consulta Comprende los patrones de ruta de acceso.
Comodines de ruta de acceso. Puedes especificar un componente de ruta de acceso como comodín:
- Mediante el asterisco
*
. Por ejemplo,foo/*
coincide con cualquier elemento secundario de un nivel de la jerarquía de nodos debajo defoo/
. - Mediante un segmento que contenga exactamente el asterisco
*
. Por ejemplo,foo/app*-us
coincide con cualquier segmento secundario debajo defoo/
con el prefijoapp
y el sufijo-us
.
Pueden establecerse coincidencias entre las rutas de acceso con comodines y varios eventos, por ejemplo, de una misma escritura. En el caso de la inserción de
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
coincide con la ruta de acceso "/foo/*"
dos veces: una vez con "hello": "world"
y otra vez con "firebase": "functions"
.
Captura de rutas de acceso. Puedes capturar coincidencias de rutas de acceso en variables con nombre para usarlas
en el código de tu función (p. ej., /user/{uid}
y /user/{uid=*-us}
).
Los valores de las variables de captura están disponibles dentro del objeto database.DatabaseEvent.params de tu función.
Comodín de instancias. También puedes especificar un
componente de la instancia con comodines. Un comodín de instancia puede tener un prefijo, sufijo o ambos (p. ej., my-app-*-prod
).
Referencia de comodín y captura
Con Cloud Functions (2ª gen.) y Realtime Database, se puede usar un patrón cuando se
especifican ref
y instance
. Cada interfaz de activador tendrá las siguientes
opciones para establecer el alcance de una función:
Especifica ref |
Especifica instance |
Comportamiento |
---|---|---|
Único (/foo/bar ) |
No se especifica | Orienta al controlador a todas las instancias en la región de la función. |
Único (/foo/bar ) |
Único (‘my-new-db' ) |
Orienta el controlador a la instancia específica en la región de la función. |
Único (/foo/bar ) |
Patrón (‘inst-prefix*' ) |
Orienta al controlador a todas las instancias que coinciden con el patrón de la región de la función. |
Patrón (/foo/{bar} ) |
No se especifica | Orienta al controlador a todas las instancias en la región de la función. |
Patrón (/foo/{bar} ) |
Único (‘my-new-db' ) |
Orienta el controlador a la instancia específica en la región de la función. |
Patrón (/foo/{bar} ) |
Patrón (‘inst-prefix*' ) |
Orienta al controlador a todas las instancias que coinciden con el patrón de la región de la función. |
Administra datos de eventos
Cuando se activa un evento de Realtime Database, se pasa un objeto Event
a la función del controlador.
Este objeto tiene una propiedad data
que, para los eventos de creación y eliminación,
contiene una instantánea de los datos creados o borrados.
En este ejemplo, la función recupera los datos de la ruta de acceso a la que se hace referencia, convierte la cadena que se encuentra en esa ubicación en mayúsculas y escribe esa cadena modificada en la base de datos:
Node.js
// 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);
},
);
Python
@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)
Lee el valor anterior
En el caso de los eventos write
o update
, la propiedad data
es un objeto Change
que contiene dos instantáneas,
las cuales representan el estado de los datos antes y después del evento de activación.
El objeto Change
tiene una propiedad before
que te permite inspeccionar lo que
se guardó en Realtime Database antes del evento y una
propiedad after
que representa
el estado de los datos después de que ocurrió el evento.
Por ejemplo, se puede usar la propiedad before
para garantizar que la función solo
convierta el texto en mayúsculas cuando se crea por primera vez:
Node.js
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); });
Python
@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)