Realtime Database-Trigger


Mit Cloud Functions können Sie Ereignisse in der Firebase Realtime Database verarbeiten, ohne den Clientcode aktualisieren zu müssen. Mit Cloud Functions können Sie Realtime Database-Vorgänge mit vollen Administratorberechtigungen ausführen und dafür sorgen, dass jede Änderung an Realtime Database einzeln verarbeitet wird. Sie können Firebase Realtime Database-Änderungen über den Daten-Snapshot oder über das Admin SDK vornehmen.

In einem typischen Lebenszyklus führt eine Firebase Realtime Database-Funktion folgende Vorgänge aus:

  1. Sie wartet auf Änderungen an einem bestimmten Realtime Database-Pfad.
  2. Sie wird ausgelöst, wenn ein Ereignis eintritt, und führt dessen Aufgaben aus.
  3. Sie erhält ein Datenobjekt, das einen Snapshot der unter diesem Pfad gespeicherten Daten enthält.

Sie können eine Funktion als Reaktion auf das Schreiben, Erstellen, Aktualisieren oder Löschen von Datenbankknoten in Firebase Realtime Database auslösen. Wenn Sie festlegen möchten, wann die Funktion ausgelöst wird, geben Sie einen der Ereignishandler und den Realtime Database-Pfad an, unter dem auf Ereignisse gewartet werden soll.

Speicherort der Funktion festlegen

Die Entfernung zwischen dem Standort einer Realtime Database-Instanz und dem Standort der Funktion kann zu einer erheblichen Netzwerklatenz führen. Außerdem kann eine Abweichung zwischen Regionen zu einem Bereitstellungsfehler führen. Um solche Situationen zu vermeiden, geben Sie den Speicherort der Funktion so an, dass er mit dem Speicherort der Datenbankinstanz übereinstimmt.

Realtime Database-Ereignisse verarbeiten

Mit Cloud Functions können Sie Realtime Database-Ereignisse mit zwei Spezifitätsgraden verarbeiten: Sie können gezielt nur nach Schreib-, Erstellungs-, Aktualisierungs- oder Löschereignissen oder nach beliebigen Änderungen jeglicher Art an einer Referenz Ausschau halten.

Folgende Handler sind für die Reaktion auf Realtime Database-Ereignisse verfügbar:

Node.js

  • onValueWritten() Wird ausgelöst, wenn Daten in Realtime Database erstellt, aktualisiert oder gelöscht werden.
  • onValueCreated() Wird nur ausgelöst, wenn Daten in Realtime Database erstellt werden.
  • onValueUpdated() Wird nur ausgelöst, wenn Daten in Realtime Database aktualisiert werden.
  • onValueDeleted() Wird nur ausgelöst, wenn Daten in Realtime Database gelöscht werden.

Python

  • on_value_written() Wird ausgelöst, wenn Daten in Realtime Database erstellt, aktualisiert oder gelöscht werden.
  • on_value_created() Wird nur ausgelöst, wenn Daten in Realtime Database erstellt werden.
  • on_value_updated() Wird nur ausgelöst, wenn Daten in Realtime Database aktualisiert werden.
  • on_value_deleted() Wird nur ausgelöst, wenn Daten in Realtime Database gelöscht werden.

Erforderliche Module importieren

Sie müssen die zu verwendenden SDK-Module in die Funktionsquelle importieren. Für dieses Beispiel müssen die HTTP- und Realtime Database-Module sowie das Firebase Admin SDK-Modul importiert werden, um in Realtime Database zu schreiben.

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()

Instanz und Pfad angeben

Wenn Sie festlegen möchten, wann und wo Ihre Funktion ausgelöst werden soll, konfigurieren Sie sie mit einem Pfad und optional einer Realtime Database-Instanz. Wenn Sie keine Instanz angeben, überwacht die Funktion alle Realtime Database-Instanzen in der Funktionsregion. Sie können auch ein Realtime Database-Instanzmuster angeben, um die Bereitstellung auf eine ausgewählte Teilmenge von Instanzen in derselben Region auszuweiten.

Beispiel:

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

Diese Parameter weisen Ihre Funktion an, Schreibvorgänge an einem bestimmten Pfad innerhalb der Realtime Database-Instanz zu verarbeiten.

Die Pfadspezifikationen gleichen alle Schreibvorgänge ab, die einen Pfad tangieren, einschließlich Schreibvorgängen, die an einem beliebigen untergeordneten Punkt auftreten. Wenn Sie als Pfad für Ihre Funktion /foo/bar festlegen, werden die Ereignisse an diesen beiden Speicherorten abgeglichen:

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

In beiden Fällen geht Firebase davon aus, dass das Ereignis unter /foo/bar auftritt, und die Ereignisdaten umfassen sowohl die alten als auch die neuen Daten unter /foo/bar. Wenn die Ereignisdaten sehr umfangreich sein können, sollten Sie erwägen, mehrere Funktionen auf tieferen Pfadebenen anstatt lediglich einer Funktion im Bereich des Datenbankstamms zu verwenden. Für optimale Leistung sollten Sie Daten nur auf der untersten Ebene anfordern.

Platzhalter und Erfassung

Sie können {key}, {key=*}, {key=prefix*} oder {key=*suffix} zum Aufnehmen verwenden. *, prefix*, *suffix für Wildcards mit einem Segment. Hinweis: ** steht für einen mehrsegmentigen Platzhalter, der von Realtime Database nicht unterstützt wird. Weitere Informationen finden Sie unter Informationen zu Pfadmustern.

Pfad-Wildcards Sie können eine Pfadkomponente als Platzhalter angeben:

  • Mit dem Sternchen * Beispiel: foo/* stimmt beispielsweise mit allen untergeordneten Elementen einer Ebene der Knotenhierarchie unter foo/ überein.
  • Verwenden Sie ein Segment, das genau ein Sternchen (*) enthält. Beispiel: foo/app*-us stimmt beispielsweise mit allen untergeordneten Segmenten unter foo/ mit dem Präfix app und dem Suffix -us überein.

Pfade mit Platzhaltern können mit mehreren Ereignissen aus einem einzelnen Schreibvorgang übereinstimmen. Die Verwendung von

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

ergibt zwei Übereinstimmungen mit dem Pfad "/foo/*": einmal "hello": "world" und einmal "firebase": "functions".

Pfaderfassung Sie können Pfadübereinstimmungen in benannte Variablen aufnehmen, die in Ihrem Funktionscode verwendet werden sollen (z.B. /user/{uid}, /user/{uid=*-us}).

Die Werte der Erfassungsvariablen sind im database.DatabaseEvent.params-Objekt Ihrer Funktion verfügbar.

Instanz-Wildcards Sie können auch eine Instanzkomponente mithilfe von Platzhaltern angeben. Ein Instanz-Wildcard kann ein Präfix, ein Suffix oder beides haben (z.B. my-app-*-prod).

Platzhalter und Erfassung

Bei Cloud Functions (2. Generation) und Realtime Database kann ein Muster verwendet werden, wenn ref und instance angegeben werden. Für jede Trigger-Benutzeroberfläche gibt es die folgenden Optionen für den Gültigkeitsbereich einer Funktion:

ref angeben instance angeben Verhalten
Einzeln (/foo/bar) Nicht angegeben Der Handler wird auf alle Instanzen in der Funktionsregion angewendet.
Einzeln (/foo/bar) Einzeln (‘my-new-db') Der Handler wird auf die spezifische Instanz in der Funktionsregion beschränkt.
Einzeln (/foo/bar) Muster (‘inst-prefix*') Der Handler wird auf alle Instanzen angewendet, die mit dem Muster in der Funktionsregion übereinstimmen.
Muster (/foo/{bar}) Nicht angegeben Der Handler wird auf alle Instanzen in der Funktionsregion angewendet.
Muster (/foo/{bar}) Einzeln (‘my-new-db') Der Handler wird auf die spezifische Instanz in der Funktionsregion beschränkt.
Muster (/foo/{bar}) Muster (‘inst-prefix*') Der Handler wird auf alle Instanzen angewendet, die mit dem Muster in der Funktionsregion übereinstimmen.

Ereignisdaten verarbeiten

Wenn ein Realtime Database-Ereignis ausgelöst wird, wird Ihrer Handlerfunktion ein Event-Objekt übergeben. Dieses Objekt hat eine data-Eigenschaft, die bei Erstellungs- und Löschereignissen einen Snapshot der erstellten oder gelöschten Daten enthält.

In diesem Beispiel ruft die Funktion die Daten für den referenzierten Pfad ab, wandelt den String an dieser Stelle in Großbuchstaben um und schreibt diesen geänderten String in die Datenbank:

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)

Vorherigen Wert lesen

Bei write- oder update-Ereignissen ist die Property data ein Change-Objekt mit zwei Snapshots, die den Datenstatus vor und nach dem auslösenden Ereignis darstellen. Das Change-Objekt hat die Property before, mit der Sie prüfen können, was vor dem Ereignis in Realtime Database gespeichert wurde. Die Property after gibt den Status der Daten nach dem Ereignis an.

Mit dem Attribut before können Sie beispielsweise festlegen, dass die Funktion Text nur beim ersten Erstellen in Großbuchstaben schreibt:

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)