了解 2023 年 Google I/O 大会上介绍的 Firebase 亮点。了解详情

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 Realtime Database-Operationen mit vollen Administratorrechten ausführen und sicherstellen, dass jede Änderung an Realtime Database einzeln verarbeitet wird. Sie können Änderungen an der Firebase Realtime Database über 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. Löst aus, wenn ein Ereignis eintritt, und führt seine Aufgaben aus (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.

Auslösen einer Echtzeit-Datenbankfunktion

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 Pfad der Echtzeitdatenbank an, in dem sie auf Ereignisse lauschen soll.

Legen Sie den Ereignishandler fest

Mit Funktionen können Sie Realtime Database-Ereignisse auf zwei Spezifitätsebenen verarbeiten; Sie können speziell nur auf Erstellungs-, Aktualisierungs- oder Löschereignisse lauschen, oder Sie können auf jede Art von Änderung an einem Pfad horchen. 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 Realtime Database 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 Realtime Database-Instanz mit instance('INSTANCE_NAME') an. Wenn Sie keine Instanz angeben, wird die Funktion in der Standardinstanz der Echtzeitdatenbank für das Firebase-Projekt bereitgestellt. Beispiel:

  • Standardinstanz der Echtzeitdatenbank: functions.database.ref('/foo/bar')
  • Instanz namens „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 Realtime Database-Instanz 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, stimmt er mit Ereignissen an diesen beiden Orten überein:

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

In beiden Fällen interpretiert Firebase, dass das Ereignis unter /foo/bar auftritt und die Ereignisdaten die alten und neuen Daten unter /foo/bar enthalten. Wenn die Ereignisdaten möglicherweise umfangreich sind, sollten Sie erwägen, mehrere Funktionen in tieferen Pfaden anstelle einer einzelnen Funktion in der Nähe des Stammverzeichnisses Ihrer Datenbank zu verwenden. Fordern Sie für die beste Leistung nur Daten auf der tiefstmöglichen Ebene an.

Sie können eine Pfadkomponente als Platzhalter angeben, indem Sie sie mit geschweiften Klammern umgeben; ref('foo/{bar}') stimmt mit jedem untergeordneten Element von /foo überein. Die Werte dieser Wildcard-Pfadkomponenten 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 Einlage von

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

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

Ereignisdaten verarbeiten

Bei der Behandlung eines Realtime Database-Ereignisses 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 onCreate und onDelete Ereignissen 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 Stelle in Großbuchstaben und schreibt diese 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 nützlich sein, um Sicherheitsregeln durchzusetzen, sodass Ihre Funktion je nach 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 Schreibvorgänge im Namen des Benutzers auszufü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 hat eine before Eigenschaft, mit der Sie überprüfen können, was vor dem Ereignis in der Realtime Database gespeichert wurde. Die before Eigenschaft gibt einen DataSnapshot zurück, in 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 bei jeder Change ist 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 Text nur in Großbuchstaben schreibt, wenn sie zum ersten Mal erstellt wird:

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