Firestore mit Cloud Functions erweitern
Mit Cloud Functions können Sie Node.js-Code bereitstellen, um Ereignisse zu verarbeiten, die durch Änderungen in Ihrer Firestore-Datenbank ausgelöst werden. Auf diese Weise können Sie problemlos serverseitige Funktionen zu Ihrer App hinzufügen, ohne Ihre eigenen Server zu betreiben.
Beispiele für Anwendungsfälle finden Sie unter Was kann ich mit Cloud Functions tun? oder Functions Samples im GitHub-Repository.
Firestore-Funktionstrigger
Das Cloud Functions for Firebase SDK exportiert ein functions.firestore
-Objekt, mit dem Sie Handler erstellen können, die mit bestimmten Firestore-Ereignissen verknüpft sind.
Ereignistyp | Trigger |
---|---|
onCreate |
Wird ausgelöst, wenn ein Dokument zum ersten Mal beschrieben wird. |
onUpdate |
Wird ausgelöst, wenn ein Dokument bereits existiert und sich ein Wert geändert hat. |
onDelete |
Wird ausgelöst, wenn ein Dokument mit Daten gelöscht wird. |
onWrite |
Wird ausgelöst, wenn onCreate , onUpdate oder onDelete ausgelöst wird. |
Wenn Sie noch kein Projekt für Cloud Functions for Firebase aktiviert haben, lesen Sie Erste Schritte: Erste Funktionen schreiben und bereitstellen zum Konfigurieren und Einrichten Ihres Cloud Functions for Firebase-Projekts.
Durch Firestore ausgelöste Funktionen schreiben
Funktions-Trigger definieren
Zum Definieren eines Cloud Firestore-Triggers müssen Sie einen Dokumentpfad und einen Ereignistyp angeben:
Node.js
const functions = require('firebase-functions');
exports.myFunction = functions.firestore
.document('my-collection/{docId}')
.onWrite((change, context) => { /* ... */ });
Dokumentpfade können entweder auf ein bestimmtes Dokument oder ein Platzhaltermuster verweisen.
Einzelnes Dokument angeben
Wenn Sie ein Ereignis für beliebige Änderungen an einem bestimmten Dokument auslösen möchten, können Sie die folgende Funktion verwenden.
Node.js
// Listen for any change on document `marie` in collection `users` exports.myFunctionName = functions.firestore .document('users/marie').onWrite((change, context) => { // ... Your code here });
Mithilfe von Platzhaltern eine Gruppe von Dokumenten angeben
Wenn Sie einen Trigger an eine Gruppe von Dokumenten anhängen möchten, z. B. an ein Dokument in einer bestimmten Sammlung, verwenden Sie {wildcard}
anstelle der Dokument-ID:
Node.js
// Listen for changes in all documents in the 'users' collection exports.useWildcard = functions.firestore .document('users/{userId}') .onWrite((change, context) => { // If we set `/users/marie` to {name: "Marie"} then // context.params.userId == "marie" // ... and ... // change.after.data() == {name: "Marie"} });
Wenn in diesem Beispiel ein Feld in einem Dokument im Verzeichnis users
geändert wird, entspricht es einem Platzhalter namens userId
.
Wenn ein Dokument in users
untergeordnete Sammlungen enthält und ein Feld in einem Dokument dieser Sammlungen geändert wird, wird der Platzhalter userId
nicht ausgelöst.
Platzhalterübereinstimmungen werden aus dem Dokumentpfad extrahiert und in context.params
gespeichert.
Sie können beliebig viele Platzhalter festlegen, um explizite Sammlungs- oder Dokument-IDs zu ersetzen. Beispiel:
Node.js
// Listen for changes in all documents in the 'users' collection and all subcollections exports.useMultipleWildcards = functions.firestore .document('users/{userId}/{messageCollectionId}/{messageId}') .onWrite((change, context) => { // If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then // context.params.userId == "marie"; // context.params.messageCollectionId == "incoming_messages"; // context.params.messageId == "134"; // ... and ... // change.after.data() == {body: "Hello"} });
Ereignis-Trigger
Beim Erstellen eines neuen Dokuments eine Funktion auslösen
Sie können auch festlegen, dass eine Funktion ausgelöst wird, wenn ein neues Dokument in einer Sammlung erstellt wird, indem Sie einen onCreate()
-Handler mit einem Platzhalter verwenden.
Diese Beispielfunktion ruft bei jedem Hinzufügen eines neuen Nutzerprofils createUser
auf:
Node.js
exports.createUser = functions.firestore .document('users/{userId}') .onCreate((snap, context) => { // Get an object representing the document // e.g. {'name': 'Marie', 'age': 66} const newValue = snap.data(); // access a particular field as you would any JS property const name = newValue.name; // perform desired operations ... });
Funktion auslösen, wenn ein Dokument aktualisiert wird
Sie können auch festlegen, dass eine Funktion ausgelöst wird, wenn ein Dokument mithilfe der onUpdate()
-Funktion mit einem Platzhalter aktualisiert wird. Diese Beispielfunktion ruft updateUser
auf, wenn ein Nutzer sein Profil ändert:
Node.js
exports.updateUser = functions.firestore .document('users/{userId}') .onUpdate((change, context) => { // Get an object representing the document // e.g. {'name': 'Marie', 'age': 66} const newValue = change.after.data(); // ...or the previous value before this update const previousValue = change.before.data(); // access a particular field as you would any JS property const name = newValue.name; // perform desired operations ... });
Funktion auslösen, wenn ein Dokument gelöscht wird
Sie können eine Funktion auch auslösen, wenn ein Dokument mit der onDelete()
-Funktion mit einem Platzhalter gelöscht wird. Diese Beispielfunktion ruft deleteUser
auf, wenn ein Nutzer sein Nutzerprofil löscht:
Node.js
exports.deleteUser = functions.firestore .document('users/{userID}') .onDelete((snap, context) => { // Get an object representing the document prior to deletion // e.g. {'name': 'Marie', 'age': 66} const deletedValue = snap.data(); // perform desired operations ... });
Funktion für alle Änderungen an einem Dokument auslösen
Wenn der Typ des Ereignisses, das ausgelöst wird, keine Rolle spielt, können Sie alle Änderungen in einem Firestore-Dokument mithilfe der onWrite()
-Funktion mit einem Platzhalter überwachen. Diese Beispielfunktion ruft modifyUser
auf, wenn ein Nutzer erstellt, aktualisiert oder gelöscht wird:
Node.js
exports.modifyUser = functions.firestore .document('users/{userID}') .onWrite((change, context) => { // Get an object with the current document value. // If the document does not exist, it has been deleted. const document = change.after.exists ? change.after.data() : null; // Get an object with the previous document value (for update or delete) const oldDocument = change.before.data(); // perform desired operations ... });
Daten lesen und schreiben
Wenn eine Funktion ausgelöst wird, stellt sie einen Snapshot der Daten bereit, die sich auf das Ereignis beziehen. Sie können diesen Snapshot verwenden, um das Dokument zu lesen oder zu schreiben, das das Ereignis ausgelöst hat, oder das Firebase Admin SDK verwenden, um auf andere Teile Ihrer Datenbank zuzugreifen.
Ereignisdaten
Daten lesen
Wenn eine Funktion ausgelöst wird, möchten Sie möglicherweise Daten aus einem Dokument abrufen, das aktualisiert wurde, oder die Daten vor der Aktualisierung abrufen. Sie können die vorherigen Daten mithilfe von change.before.data()
abrufen, das den Dokument-Snapshot vor der Aktualisierung enthält.
Entsprechend enthält change.after.data()
den Status des Dokument-Snapshots nach der Aktualisierung.
Node.js
exports.updateUser2 = functions.firestore .document('users/{userId}') .onUpdate((change, context) => { // Get an object representing the current document const newValue = change.after.data(); // ...or the previous value before this update const previousValue = change.before.data(); });
Sie können auf Attribute wie in jedem anderen Objekt zugreifen. Alternativ können Sie die Funktion get
für den Zugriff auf bestimmte Felder verwenden:
Node.js
// Fetch data using standard accessors const age = snap.data().age; const name = snap.data()['name']; // Fetch data using built in accessor const experience = snap.get('experience');
Daten schreiben
Jeder Funktionsaufruf ist einem bestimmten Dokument in Ihrer Firestore-Datenbank zugeordnet. Sie können auf dieses Dokument als DocumentReference
im ref
-Attribut des an Ihre Funktion zurückgegebenen Snapshots zugreifen.
DocumentReference
ist Teil des Firestore Node.js SDK und enthält Methoden wie update()
, set()
und remove()
. Damit können Sie das Dokument, das die Funktion ausgelöst hat, auf einfache Weise ändern.
Node.js
// Listen for updates to any `user` document. exports.countNameChanges = functions.firestore .document('users/{userId}') .onUpdate((change, context) => { // Retrieve the current and previous value const data = change.after.data(); const previousData = change.before.data(); // We'll only update if the name has changed. // This is crucial to prevent infinite loops. if (data.name == previousData.name) { return null; } // Retrieve the current count of name changes let count = data.name_change_count; if (!count) { count = 0; } // Then return a promise of a set operation to update the count return change.after.ref.set({ name_change_count: count + 1 }, {merge: true}); });
Daten außerhalb des Triggerereignisses
Cloud Functions wird in einer vertrauenswürdigen Umgebung ausgeführt und ist als Dienstkonto für Ihr Projekt autorisiert. Sie können Lese- und Schreibvorgänge mit dem Firebase Admin SDK durchführen:
Node.js
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.writeToFirestore = functions.firestore
.document('some/doc')
.onWrite((change, context) => {
db.doc('some/otherdoc').set({ ... });
});
Beschränkungen
Beachten Sie die folgenden Einschränkungen für Firestore-Trigger für Cloud Run Functions:
- Cloud Run Functions (1. Generation) erfordert eine vorhandene „(default)“-Datenbank im nativen Firestore-Modus. Es unterstützt keine benannten Firestore-Datenbanken und keinen Datastore-Modus. Verwenden Sie in solchen Fällen Cloud Run Functions (2. Generation), um Ereignisse zu konfigurieren.
- Die Reihenfolge ist nicht garantiert. Schnelle Änderungen können Funktionsaufrufe in einer unvorhergesehenen Reihenfolge auslösen.
- Ereignisse werden mindestens einmal übergeben. Ein einzelnes Ereignis kann aber zu mehreren Funktionsaufrufen führen. Vermeiden Sie die Abhängigkeit von genau einmal vorkommenden Verfahren und schreiben Sie idempotente Funktionen.
- Firestore im Datastore-Modus erfordert Cloud Run Functions (2. Generation). Cloud Run Functions (1. Generation) unterstützt den Datastore-Modus nicht.
- Ein Trigger ist mit einer einzelnen Datenbank verknüpft. Sie können keinen Trigger erstellen, der mit mehreren Datenbanken übereinstimmt.
- Wenn Sie eine Datenbank löschen, werden nicht automatisch die Trigger für diese Datenbank gelöscht. Der Trigger sendet keine Ereignisse mehr, bleibt aber bestehen, bis Sie ihn löschen.
- Wenn ein übereinstimmendes Ereignis die maximale Anfragegröße überschreitet, wird es möglicherweise nicht an Cloud Run Functions der 1. Generation gesendet.
- Ereignisse, die aufgrund der Anfragegröße nicht gesendet wurden, werden in Plattform-Logs protokolliert und auf die Lognutzung für das Projekt angerechnet.
- Sie finden diese Logs im Log-Explorer mit der Meldung "Ereignis kann nicht an die Cloud Functions-Funktion gesendet werden, da die Größe das Limit für die 1. Generation überschreitet..." mit dem Schweregrad
error
. Den Funktionsnamen finden Sie im FeldfunctionName
. Wenn das FeldreceiveTimestamp
noch eine Stunde gültig ist, können Sie den tatsächlichen Ereignisinhalt ableiten, indem Sie das betreffende Dokument mit einem Snapshot vor und nach dem Zeitstempel lesen. - So können Sie das vermeiden:
- Migrieren und Upgrade auf Cloud Run Functions (2. Generation)
- Dokument verkleinern
- Die betreffenden Cloud Run Functions-Funktionen löschen
- Sie können die Protokollierung selbst mithilfe von Ausschlüssen deaktivieren. Die betreffenden Ereignisse werden jedoch weiterhin nicht gesendet.