Echtzeit-Datenbank-Trigger


Mit Cloud Functions können Sie Ereignisse in der Firebase-Echtzeitdatenbank verarbeiten, ohne den Clientcode aktualisieren zu müssen. Mit Cloud Functions können Sie Echtzeitdatenbankvorgänge mit vollständigen Administratorrechten ausführen und stellen sicher, dass jede Änderung an der Echtzeitdatenbank einzeln verarbeitet wird. Sie können Änderungen an der Firebase-Echtzeitdatenbank über den DataSnapshot oder über das Admin SDK vornehmen.

In einem typischen Lebenszyklus führt eine Firebase Realtime Database-Funktion Folgendes aus:

  1. Wartet auf Änderungen an einem bestimmten Speicherort der Echtzeitdatenbank.
  2. Wird ausgelöst, wenn ein Ereignis eintritt und seine Aufgaben ausführt (Beispiele für Anwendungsfälle finden Sie unter „Was kann ich mit Cloud Functions tun?“ ).
  3. Empfängt ein Datenobjekt, das eine Momentaufnahme der im angegebenen Dokument gespeicherten Daten enthält.

Lösen Sie eine Echtzeitdatenbankfunktion aus

Erstellen Sie mit functions.database neue Funktionen für Echtzeitdatenbankereignisse. Um zu steuern, wann die Funktion ausgelöst wird, geben Sie einen der Ereignishandler und den Echtzeitdatenbankpfad an, in dem auf Ereignisse gewartet wird.

Legen Sie den Ereignishandler fest

Mithilfe von Funktionen können Sie Echtzeitdatenbankereignisse auf zwei Spezifitätsebenen verarbeiten. Sie können gezielt nur auf Erstellungs-, Aktualisierungs- oder Löschereignisse warten, oder Sie können auf jede Änderung jeglicher Art an einem Pfad warten. Cloud Functions unterstützt diese Event-Handler für Realtime Database:

  • onWrite() , das ausgelöst wird, wenn Daten in der Echtzeitdatenbank erstellt, aktualisiert oder gelöscht werden.
  • onCreate() , das ausgelöst wird, wenn neue Daten in der Echtzeitdatenbank erstellt werden.
  • onUpdate() , das ausgelöst wird, wenn Daten in der Echtzeitdatenbank aktualisiert werden.
  • onDelete() , das ausgelöst wird, wenn Daten aus der Echtzeitdatenbank gelöscht werden.

Geben Sie die Instanz und den Pfad an

Um zu steuern, wann und wo Ihre Funktion ausgelöst werden soll, rufen Sie ref(path) auf, um einen Pfad anzugeben, und geben Sie optional eine Echtzeitdatenbankinstanz mit instance('INSTANCE_NAME') an. Wenn Sie keine Instanz angeben, wird die Funktion in der Standard-Echtzeitdatenbankinstanz für das Firebase-Projekt bereitgestellt. Beispiel:

  • Standard-Echtzeitdatenbankinstanz: functions.database.ref('/foo/bar')
  • Instanz mit dem Namen „my-app-db-2“: functions.database.instance('my-app-db-2').ref('/foo/bar')

Diese Methoden weisen Ihre Funktion an, Schreibvorgänge an einem bestimmten Pfad innerhalb der Echtzeitdatenbankinstanz zu verarbeiten. Pfadspezifikationen stimmen mit allen Schreibvorgängen überein, die einen Pfad berühren, einschließlich Schreibvorgängen, die irgendwo darunter stattfinden. Wenn Sie den Pfad für Ihre Funktion als /foo/bar festlegen, werden Ereignisse an diesen beiden Orten abgeglichen:

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

In beiden Fällen interpretiert Firebase, dass das Ereignis unter /foo/bar auftritt, und die Ereignisdaten umfassen die alten und neuen Daten unter /foo/bar . Wenn die Ereignisdaten möglicherweise umfangreich sind, sollten Sie die Verwendung mehrerer Funktionen in tieferen Pfaden anstelle einer einzelnen Funktion in der Nähe des Stammverzeichnisses Ihrer Datenbank in Betracht ziehen. Um die beste Leistung zu erzielen, fordern Sie Daten nur auf der tiefstmöglichen Ebene an.

Sie können eine Pfadkomponente als Platzhalter angeben, indem Sie sie in geschweifte Klammern einschließen. ref('foo/{bar}') stimmt mit jedem untergeordneten Element von /foo überein. Die Werte dieser Platzhalterpfadkomponenten sind im EventContext.params Objekt Ihrer Funktion verfügbar. In diesem Beispiel ist der Wert als context.params.bar verfügbar.

Pfade mit Platzhaltern können mit mehreren Ereignissen aus einem einzigen Schreibvorgang übereinstimmen. Eine Einfügung von

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

stimmt zweimal mit dem Pfad "/foo/{bar}" überein: einmal mit "hello": "world" und noch einmal mit "firebase": "functions" .

Behandeln Sie Ereignisdaten

Bei der Verarbeitung eines Echtzeitdatenbankereignisses ist das zurückgegebene Datenobjekt ein DataSnapshot . Bei onWrite oder onUpdate Ereignissen ist der erste Parameter ein Change Objekt, das zwei Snapshots enthält, die den Datenstatus vor und nach dem auslösenden Ereignis darstellen. Bei den Ereignissen onCreate und onDelete ist das zurückgegebene Datenobjekt eine Momentaufnahme der erstellten oder gelöschten Daten.

In diesem Beispiel ruft die Funktion den Snapshot für den angegebenen Pfad ab, konvertiert die Zeichenfolge an dieser Position in Großbuchstaben und schreibt die geänderte Zeichenfolge in die Datenbank:

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

Zugriff auf Benutzerauthentifizierungsinformationen

Über EventContext.auth und EventContext.authType können Sie auf die Benutzerinformationen, einschließlich Berechtigungen, für den Benutzer zugreifen, der eine Funktion ausgelöst hat. Dies kann für die Durchsetzung von Sicherheitsregeln nützlich sein, sodass Ihre Funktion basierend auf der Berechtigungsstufe des Benutzers verschiedene Vorgänge ausführen kann:

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

Außerdem können Sie Benutzerauthentifizierungsinformationen nutzen, um sich als Benutzer auszugeben und im Namen des Benutzers Schreibvorgänge durchzuführen. Stellen Sie sicher, dass Sie die App-Instanz wie unten gezeigt löschen, um Parallelitätsprobleme zu vermeiden:

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

Lesen des vorherigen Werts

Das Change Objekt verfügt über eine before Eigenschaft, mit der Sie überprüfen können, was vor dem Ereignis in der Echtzeitdatenbank gespeichert wurde. Die Eigenschaft before gibt einen DataSnapshot zurück, bei dem alle Methoden (z. B. val() und exists() ) auf den vorherigen Wert verweisen. Sie können den neuen Wert erneut lesen, indem Sie entweder den ursprünglichen DataSnapshot verwenden oder die after Eigenschaft lesen. Diese Eigenschaft ist bei jeder Change ein weiterer DataSnapshot der den Status der Daten nach dem Eintreten des Ereignisses darstellt.

Beispielsweise kann die Eigenschaft before verwendet werden, um sicherzustellen, dass die Funktion beim ersten Erstellen nur Text in Großbuchstaben schreibt:

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