Dzięki Cloud Functions możesz wdrożyć kod Node.js do obsługi zdarzeń wywoływanych przez zmiany w bazie danych Cloud Firestore. Dzięki temu możesz łatwo dodawać do aplikacji funkcje po stronie serwera bez konieczności uruchamiania własnych serwerów.
Przykłady zastosowań znajdziesz w artykule Co mogę zrobić za pomocą Cloud Functions?. lub w repozytorium Functions Samples na GitHubie.
Aktywatory funkcji Cloud Firestore
Pakiet SDK Cloud Functions for Firebase eksportuje obiekt functions.firestore, który umożliwia tworzenie modułów obsługi powiązanych z konkretnymi zdarzeniami Cloud Firestore.
| Typ zdarzenia | Aktywator |
|---|---|
onCreate |
Uruchamiana, gdy dokument jest zapisywany po raz pierwszy. |
onUpdate |
Uruchamiane, gdy dokument już istnieje i zmieniła się dowolna wartość. |
onDelete |
Uruchamiane, gdy dokument z danymi zostanie usunięty. |
onWrite |
Wywoływane, gdy wywoływane są reguły onCreate, onUpdate lub onDelete. |
Jeśli nie masz jeszcze projektu z włączoną usługą Cloud Functions for Firebase, przeczytaj artykuł Pierwsze kroki: pisanie i wdrażanie pierwszych funkcji, aby dowiedzieć się, jak skonfigurować projekt Cloud Functions for Firebase.
Pisanie funkcji wywoływanych przez Cloud Firestore
Definiowanie aktywatora funkcji
Aby zdefiniować aktywator Cloud Firestore, określ ścieżkę dokumentu i typ zdarzenia:
Node.js
const functions = require('firebase-functions');
exports.myFunction = functions.firestore
.document('my-collection/{docId}')
.onWrite((change, context) => { /* ... */ });
Ścieżki dokumentów mogą odwoływać się do konkretnego dokumentu lub wzorca wieloznacznego.
Określ pojedynczy dokument
Jeśli chcesz wywołać zdarzenie w przypadku dowolnej zmiany w konkretnym dokumencie, możesz użyć tej funkcji.
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 });
Określanie grupy dokumentów za pomocą symboli wieloznacznych
Jeśli chcesz dołączyć aktywator do grupy dokumentów, np. do dowolnego dokumentu w określonej kolekcji, zamiast identyfikatora dokumentu użyj {wildcard}:
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"} });
W tym przykładzie, gdy dowolne pole w dowolnym dokumencie w kolekcji users zostanie zmienione, będzie pasować do symbolu wieloznacznego o nazwie userId.
Jeśli dokument w kolekcji users ma podkolekcje i zmieni się pole w dokumencie w jednej z tych podkolekcji, symbol wieloznaczny userId nie zostanie aktywowany.
Dopasowania symboli wieloznacznych są wyodrębniane ze ścieżki dokumentu i przechowywane w context.params.
Możesz zdefiniować dowolną liczbę symboli wieloznacznych, aby zastąpić jawne identyfikatory kolekcji lub dokumentów, na przykład:
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"} });
Aktywatory zdarzeń
Wywoływanie funkcji po utworzeniu nowego dokumentu
Możesz wywołać funkcję za każdym razem, gdy w kolekcji zostanie utworzony nowy dokument, używając modułu obsługi onCreate() z symbolem wieloznacznym.
Ta przykładowa funkcja wywołuje createUser za każdym razem, gdy dodawany jest nowy profil użytkownika:
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 ... });
Uruchamianie funkcji po zaktualizowaniu dokumentu
Możesz też wywołać funkcję, która zostanie uruchomiona po zaktualizowaniu dokumentu, używając funkcji onUpdate() z symbolem wieloznacznym. Ta przykładowa funkcja wywołuje updateUser, jeśli użytkownik zmieni swój profil:
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 ... });
Uruchamianie funkcji po usunięciu dokumentu
Możesz też wywołać funkcję, gdy dokument zostanie usunięty, używając funkcji onDelete() z symbolem wieloznacznym. Ta przykładowa funkcja wywołuje deleteUser, gdy użytkownik usuwa swój profil:
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 ... });
Aktywowanie funkcji w przypadku wszystkich zmian w dokumencie
Jeśli nie interesuje Cię typ wywoływanego zdarzenia, możesz nasłuchiwać wszystkich zmian w dokumencie Cloud Firestore za pomocą funkcji onWrite() z symbolem wieloznacznym. Ta przykładowa funkcja wywołuje modifyUser, gdy użytkownik zostanie utworzony, zaktualizowany lub usunięty:
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 ... });
Odczytywanie i zapisywanie danych
Po aktywowaniu funkcja udostępnia zrzut danych związanych ze zdarzeniem. Możesz użyć tego zrzutu, aby odczytać lub zapisać dokument, który wywołał zdarzenie, lub użyć pakietu Firebase Admin SDK, aby uzyskać dostęp do innych części bazy danych.
Dane zdarzenia
Dane na temat czytania
Po aktywowaniu funkcji możesz pobrać dane ze zaktualizowanego dokumentu lub dane sprzed aktualizacji. Możesz uzyskać wcześniejsze dane, używając change.before.data(), który zawiera migawkę dokumentu sprzed aktualizacji.
Podobnie change.after.data() zawiera stan zrzutu dokumentu po aktualizacji.
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(); });
Dostęp do właściwości możesz uzyskać tak samo jak w przypadku każdego innego obiektu. Możesz też użyć funkcji get, aby uzyskać dostęp do konkretnych pól:
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');
Zapisywanie danych
Każde wywołanie funkcji jest powiązane z konkretnym dokumentem w bazie danych Cloud Firestore. Możesz uzyskać dostęp do tego dokumentu jako DocumentReference we właściwości ref zrzutu zwróconego do funkcji.
Ten obiekt DocumentReference pochodzi z Cloud Firestorepakietu SDK Node.js i zawiera metody takie jak update(), set() i remove(), dzięki którym możesz łatwo zmodyfikować dokument, który wywołał funkcję.
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}); });
Dane spoza zdarzenia wywołującego
Funkcje Cloud Functions są wykonywane w zaufanym środowisku, co oznacza, że są autoryzowane jako konto usługi w Twoim projekcie. Możesz wykonywać operacje odczytu i zapisu za pomocą pakietu Firebase Admin SDK:
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({ ... });
});
Ograniczenia
Pamiętaj o tych ograniczeniach dotyczących reguł Cloud Firestore w przypadku Cloud Functions:
- Cloud Functions (1 generacji) wymaga istniejącej bazy danych „(default)” w trybie natywnym Firestore. Nie obsługuje baz danych o nazwie Cloud Firestore ani trybu Datastore. W takich przypadkach do konfigurowania zdarzeń używaj Cloud Functions (2 generacji).
- Konfiguracja między projektami z aktywatorami Cloud Functions i Cloud Firestore jest ograniczona. Aby skonfigurować aktywator Cloud Firestore, Cloud Functions musi być w tym samym projekcie.
- Kolejność nie jest gwarantowana. Szybkie zmiany mogą spowodować wywołanie funkcji w nieoczekiwanej kolejności.
- Zdarzenia są dostarczane co najmniej raz, ale pojedyncze zdarzenie może spowodować wielokrotne wywołanie funkcji. Unikaj polegania na mechanizmach gwarantujących dostarczenie dokładnie raz i pisz funkcje idempotentne.
- Cloud Firestore w trybie Datastore wymaga Cloud Functions (2 generacji). Cloud Functions (1 generacji) nie obsługuje trybu Datastore.
- Aktywator jest powiązany z jedną bazą danych. Nie możesz utworzyć aktywatora, który pasuje do wielu baz danych.
- Usunięcie bazy danych nie powoduje automatycznego usunięcia żadnych aktywatorów tej bazy danych. Aktywator przestanie dostarczać zdarzenia, ale będzie nadal istnieć, dopóki go nie usuniesz.
- Jeśli dopasowane zdarzenie przekracza maksymalny rozmiar żądania, może nie zostać dostarczone do Cloud Functions (1 generacji).
- Zdarzenia, które nie zostały dostarczone z powodu rozmiaru żądania, są rejestrowane w logach platformy i wliczane do wykorzystania logów w projekcie.
- Te logi znajdziesz w Eksploratorze logów z komunikatem „Nie można dostarczyć zdarzenia do funkcji w Cloud Functions, ponieważ rozmiar przekracza limit dla 1 generacji…” o ważności
error. Nazwę funkcji znajdziesz w polufunctionName. Jeśli polereceiveTimestampnadal mieści się w ciągu godziny od teraz, możesz wywnioskować rzeczywistą treść zdarzenia, odczytując dany dokument z migawką przed sygnaturą czasową i po niej. - Aby uniknąć takiej sytuacji:
- Migracja i uaktualnienie do Cloud Functions (2 generacji)
- Zmniejsz rozmiar dokumentu.
- Usuń dany element Cloud Functions
- Możesz wyłączyć samo logowanie za pomocą wykluczeń, ale pamiętaj, że zdarzenia powodujące błędy nadal nie będą dostarczane.