Activadores de Realtime Database

En un ciclo de vida típico, una función de Firebase Realtime Database hace lo siguiente:

  1. Espera cambios en una ruta de acceso de Realtime Database específica.
  2. Se activa cuando ocurre un evento y realiza sus tareas.
  3. 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.

Activa una función cuando se realicen cambios en Firebase Realtime Database

Usa el subpaquete firebase-functions/v2/database para crear una función que controle eventos de 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 a fin de 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:

  • onValueWritten(): Solo se activa cuando se escriben 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.

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 se implementa 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.

Usemos onValueWritten() a modo de ejemplo:

# 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) => {
    // …
  }
);

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 RTDB. 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 de foo/.
  • Mediante un segmento que contenga exactamente el asterisco *. Por ejemplo, foo/app*-us coincide con cualquier segmento secundario debajo de foo/ con el prefijo app 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 (2nd 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 controla un evento de Realtime Database, el objeto de datos que se muestra es una DataSnapshot.

En el caso de los eventos onValueWritten o onValueUpdated, el primer parámetro 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.

En el caso de los eventos onValueCreated y onValueDeleted, el objeto de datos que se muestra es una instantánea de los datos creados o borrados.

En este ejemplo, la función recupera la instantánea de la ruta de acceso foo/bar especificada como snap, convierte a mayúsculas la string que se encuentra en esa ubicación y escribe la string modificada en la base de datos:

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

Lee el valor anterior

El objeto Change tiene una propiedad before que te permite inspeccionar lo que se guardó en Realtime Database antes del evento. La propiedad before muestra una DataSnapshot en la que todos los métodos (por ejemplo, val() y exists()) hacen referencia al valor anterior. Puedes volver a leer el valor nuevo si usas la DataSnapshot original o lees la propiedad after. Esta propiedad, presente en todo Change, es otra DataSnapshot que representa el estado de los datos después del evento.

Por ejemplo, se puede usar la propiedad before para garantizar que la función solo transforme el texto a mayúsculas cuando se crea por primera 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);
        });