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

Activadores de base de datos en tiempo real

Organiza tus páginas con colecciones Guarda y categoriza el contenido según tus preferencias.

Con Cloud Functions, puede manejar eventos en Firebase Realtime Database sin necesidad de actualizar el código del cliente. Cloud Functions le permite ejecutar operaciones de Realtime Database con todos los privilegios administrativos y garantiza que cada cambio en Realtime Database se procese individualmente. Puede realizar cambios en la base de datos en tiempo real de Firebase a través de DataSnapshot o del SDK de administrador .

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

  1. Espera los cambios en una ubicación particular de Realtime Database.
  2. Se activa cuando ocurre un evento y realiza sus tareas (consulte ¿Qué puedo hacer con Cloud Functions? para obtener ejemplos de casos de uso).
  3. Recibe un objeto de datos que contiene una instantánea de los datos almacenados en el documento especificado.

Activar una función de base de datos en tiempo real

Cree nuevas funciones para eventos de Realtime Database con functions.database . Para controlar cuándo se activa la función, especifique uno de los controladores de eventos y especifique la ruta de Realtime Database donde escuchará los eventos.

Establecer el controlador de eventos

Las funciones le permiten manejar eventos de Realtime Database en dos niveles de especificidad; puede escuchar específicamente solo los eventos de creación, actualización o eliminación, o puede escuchar cualquier cambio de cualquier tipo en una ruta. Cloud Functions admite estos controladores de eventos para Realtime Database:

  • onWrite() , que se activa cuando se crean, actualizan o eliminan datos en Realtime Database.
  • onCreate() , que se activa cuando se crean nuevos datos en Realtime Database.
  • onUpdate() , que se activa cuando se actualizan los datos en Realtime Database .
  • onDelete() , que se activa cuando se eliminan datos de Realtime Database .

Especificar la instancia y la ruta

Para controlar cuándo y dónde debe activarse su función, llame ref(path) para especificar una ruta y, opcionalmente, especifique una instancia de Realtime Database con instance('INSTANCE_NAME') . Si no especifica una instancia, la función se implementa en la instancia predeterminada de Realtime Database para el proyecto de Firebase. Por ejemplo:

  • Instancia predeterminada de base de datos en tiempo real: functions.database.ref('/foo/bar')
  • Instancia denominada "my-app-db-2": functions.database.instance('my-app-db-2').ref('/foo/bar')

Estos métodos dirigen su función para manejar escrituras en una determinada ruta dentro de la instancia de Realtime Database. Las especificaciones de la ruta coinciden con todas las escrituras que tocan una ruta, incluidas las escrituras que ocurren en cualquier lugar debajo de ella. Si configura la ruta para su función como /foo/bar , coincide con los eventos en estas dos ubicaciones:

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

En cualquier caso, Firebase interpreta que el evento ocurre en /foo/bar y que los datos del evento incluyen los datos antiguos y nuevos en /foo/bar . Si los datos de eventos pueden ser grandes, considere usar múltiples funciones en rutas más profundas en lugar de una sola función cerca de la raíz de su base de datos. Para obtener el mejor rendimiento, solo solicite datos en el nivel más profundo posible.

Puede especificar un componente de ruta como comodín rodeándolo con corchetes; ref('foo/{bar}') coincide con cualquier elemento secundario de /foo . Los valores de estos componentes de ruta comodín están disponibles dentro del objeto EventContext.params de su función. En este ejemplo, el valor está disponible como context.params.bar .

Las rutas con comodines pueden coincidir con varios eventos de una sola escritura. un inserto de

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

coincide con la ruta "/foo/{bar}" dos veces: una vez con "hello": "world" y otra vez con "firebase": "functions" .

Manejar datos de eventos

Al manejar un evento de Realtime Database, el objeto de datos devuelto es un DataSnapshot . Para los eventos onWrite o onUpdate , el primer parámetro es un objeto Change que contiene dos instantáneas que representan el estado de los datos antes y después del evento desencadenante. Para los eventos onCreate y onDelete , el objeto de datos devuelto es una instantánea de los datos creados o eliminados.

En este ejemplo, la función recupera la instantánea de la ruta especificada como snap , convierte la cadena en esa ubicación a mayúsculas y escribe esa cadena 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
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);
    });

Acceder a la información de autenticación del usuario

Desde EventContext.auth y EventContext.authType , puede acceder a la información del usuario, incluidos los permisos, para el usuario que activó una función. Esto puede ser útil para hacer cumplir las reglas de seguridad, lo que permite que su función complete diferentes operaciones según el nivel de permisos del usuario:

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

Además, puede aprovechar la información de autenticación del usuario para "suplantar" a un usuario y realizar operaciones de escritura en nombre del usuario. Asegúrese de eliminar la instancia de la aplicación como se muestra a continuación para evitar problemas de concurrencia:

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

Lectura del valor anterior

El objeto Change tiene una propiedad before que le permite inspeccionar lo que se guardó en Realtime Database antes del evento. La propiedad before devuelve una DataSnapshot donde todos los métodos (por ejemplo, val() y exists() ) hacen referencia al valor anterior. Puede volver a leer el nuevo valor utilizando la DataSnapshot original o leyendo la propiedad after . Esta propiedad en cualquier Change es otra DataSnapshot que representa el estado de los datos después de que ocurrió el evento.

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