Mit Cloud Functions können Sie Ereignisse in der Firebase Realtime Database verarbeiten, ohne Clientcode aktualisieren zu müssen. Cloud Functions ermöglicht die Ausführung von Realtime Database-Vorgängen mit vollen Administrator berechtigungen und stellt sicher, dass jede Änderung an Realtime Database einzeln verarbeitet wird. Sie können Firebase Realtime Database Änderungen über den Datensnapshot oder über das Admin SDK vornehmen.
In einem typischen Lebenszyklus führt eine Firebase Realtime Database Funktion folgende Vorgänge aus:
- Sie wartet auf Änderungen an einem bestimmten Realtime Database Pfad.
- Sie wird ausgelöst, wenn ein Ereignis eintritt, und führt dessen Aufgaben aus.
- Sie empfängt ein Datenobjekt, das einen Snapshot der an 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 werden soll, geben Sie einen der Ereignishandler und geben Sie den Realtime Database Pfad an, an dem sie auf Ereignisse wartet.
Funktionsstandort 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 den Regionen zu einem Bereitstellungsfehler führen. Um diese Situationen zu vermeiden, geben Sie den Funktionsstandort so an, dass er mit dem Standort 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.
Die folgenden 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
Im Quellcode Ihrer Funktion müssen Sie die SDK-Module importieren, die Sie verwenden möchten. Für dieses Beispiel müssen Sie HTTP- und Realtime Database Module sowie das Firebase Admin SDK Modul zum Schreiben in Realtime Database importieren.
Node.js
// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onRequest} = require("firebase-functions/https");
const {onValueCreated} = require("firebase-functions/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, wartet die Funktion auf alle Realtime Database Instanzen in der Funktionsregion. Sie können auch ein Realtime Database Instanzmuster angeben, um die Bereitstellung für eine ausgewählte Teilmenge von Instanzen in derselben Region durchzuführen.
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 den Pfad für Ihre Funktion auf /foo/bar festlegen, werden Ereignisse an beiden folgenden Speicherorten 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 enthalten die alten und 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 eine optimale Leistung sollten Sie Daten nur auf der tiefsten möglichen Ebene anfordern.
Platzhalter und Erfassung
Sie können {key}, {key=*}, {key=prefix*}, {key=*suffix} für die Erfassung verwenden. *, prefix*, *suffix für Platzhalter für ein einzelnes Segment.
Hinweis: ** steht für Platzhalter für mehrere Segmente, was von Realtime Database nicht unterstützt wird.
Weitere Informationen finden Sie unter Informationen zu Pfadmustern.
Platzhalter für Pfade Sie können eine Pfadkomponente als Platzhalter angeben:
- Mit einem Sternchen (
*). Beispiel:foo/*entspricht allen untergeordneten Elementen eine Ebene unter der Knotenhierarchie unterfoo/. - Mit einem Segment, das genau ein Sternchen (
*) enthält. Beispiel:foo/app*-usentspricht allen untergeordneten Segmenten unterfoo/mit dem Präfixappund dem Suffix-us.
Pfade mit Platzhaltern können mit mehreren Ereignissen aus beispielsweise einem einzigen Schreibvorgang übereinstimmen. Ein Einfügen von
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
ergibt zwei Übereinstimmungen mit dem Pfad "/foo/*": einmal mit "hello": "world" und einmal mit
"firebase": "functions".
Pfaderfassung Sie können Pfadübereinstimmungen in benannten Variablen erfassen, 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.
Platzhalter für Instanzen Sie können auch eine Instanzkomponente mit Platzhaltern angeben. Ein Instanzplatzhalter kann ein Präfix, ein Suffix oder beides haben (z.B. my-app-*-prod).
Referenz für Platzhalter und Erfassung
Mit Cloud Functions (2. Generation) und Realtime Database kann beim
Angeben von ref und instance ein Muster verwendet werden. Jede Triggerschnittstelle bietet die folgenden Optionen zum Festlegen des Umfangs einer Funktion:
Angeben ref |
Angeben instance |
Verhalten |
|---|---|---|
Einzeln (/foo/bar) |
Nicht angegeben | Der Handler wird auf alle Instanzen in der Funktionsregion beschränkt. |
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 beschränkt, die dem Muster in der Funktionsregion entsprechen. |
Muster (/foo/{bar}) |
Nicht angegeben | Der Handler wird auf alle Instanzen in der Funktionsregion beschränkt. |
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 beschränkt, die dem Muster in der Funktionsregion entsprechen. |
Ereignisdaten verarbeiten
Wenn ein Realtime Database Ereignis ausgelöst wird, wird ein Event Objekt an Ihre Handler-Funktion übergeben.
Dieses Objekt hat eine data-Property, die für Erstellungs- und Löschereignisse 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 diesem Speicherort 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 data-Property ein Change-Objekt, das zwei Snapshots enthält, die den Datenstatus vor und nach dem auslösenden Ereignis darstellen.
Das Change Objekt hat eine before Property, mit der Sie prüfen können, was
in der Realtime Database vor dem Ereignis gespeichert wurde, und eine
after Property, die
den Status der Daten nach dem Ereignis darstellt.
Die before-Property kann beispielsweise verwendet werden, um sicherzustellen, dass die Funktion Text nur dann in Großbuchstaben umwandelt, wenn er zum ersten Mal erstellt wird:
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)
Auf Authentifizierungskontext zugreifen
Bei Funktionen, die durch RTDB-Eventarc-Ereignisse ausgelöst werden, ist der Authentifizierungskontext in der Ereignisnutzlast enthalten:
- authtype: Der Typ des Prinzipals, der das Ereignis ausgelöst hat. Mögliche Werte sind:
app_user: Ein Endnutzer der Anwendung des Entwicklers.admin: Ein Dienstkonto.unauthenticated: Ein nicht authentifizierter Nutzer.unknown: Standardwert, wenn keine Authentifizierungsinformationen verfügbar sind.
- authid: Die eindeutige Kennung des Prinzipals.
- Wenn
authtypeapp_userist, ist dies die UID des Nutzers. - Wenn
authtypeadminist, ist dies die E-Mail-Adresse des Dienstkontos oder des IAM-Nutzers.
- Wenn
Dieser Code wandelt den Text der Nachricht nur dann in Großbuchstaben um, wenn der Nutzer, der die Funktion ausgelöst hat, kein Administrator ist. Außerdem prüft der Code, ob der Nutzer, der die Nachricht ausgelöst hat, der tatsächliche Absender der Nachricht ist.
Node.js
// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onValueWritten} = require("firebase-functions/v2/database");
const {logger} = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.dbtrigger = onValueWritten("/messages/{pushId}/original", async (event) => {
// 1. Check whether authtype is admin. If it is, skip this operation.
if (event.authType === "admin") {
logger.log("Modification by admin detected. Skipping uppercase conversion.");
return null;
}
// 2. Retrieve the userID of the sender (assumed sibling node 'senderId')
const snapshot = await event.data.after.ref.parent.child("senderId").get();
const senderId = snapshot.val();
// 3. Check if userID of sender of message = event.authid
if (senderId !== event.authId) {
logger.error(`Unauthorized write: senderId (${senderId}) does not match authId (${event.authId})`);
return null;
}
// Grab the value that was written to the Realtime Database.
const original = event.data.after.val();
logger.log("Uppercasing", event.params.pushId, original);
const uppercase = original.toUpperCase();
// Return the promise to set the "uppercase" sibling node.
return event.data.after.ref.parent.child("uppercase").set(uppercase);
});
Python
from firebase_functions import db_fn
from firebase_admin import initialize_app, db
initialize_app()
@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[db_fn.Change]) -> None:
# 1. Check whether authtype is admin. If it is, skip this operation.
if event.auth_type == "admin":
print("Admin user detected. Skipping.")
return
# 2. Retrieve the userID of the sender (assumed sibling node: 'senderId')
parent_ref = db.reference(event.reference).parent
sender_id = parent_ref.child("senderId").get()
# 3. Check if userID of sender = event.auth_id
if sender_id != event.auth_id:
print(f"Unauthorized: sender_id {sender_id} != auth_id {event.auth_id}")
return
# Exit when the data is deleted.
if event.data.after is None:
return
# Grab the value and uppercase it
original = event.data.after
if not isinstance(original, str):
return
print(f"Uppercasing {event.params['pushId']}: {original}")
upper = original.upper()
parent_ref.child("uppercase").set(upper)