Dzięki Cloud Functions możesz obsługiwać zdarzenia w Firebase Realtime Database bez konieczności aktualizowania kodu klienta.
Cloud Functions umożliwia wykonywanie operacji Realtime Database z pełnymi uprawnieniami administracyjnymi i zapewnia, że każda zmiana w Realtime Database jest przetwarzana indywidualnie. Możesz wprowadzić zmiany w Firebase Realtime Database za pomocą DataSnapshot lub pakietu Admin SDK.
W typowym cyklu życia funkcja Firebase Realtime Database wykonuje te czynności:
- Czeka na zmiany w określonej lokalizacji Realtime Database.
- Uruchamia się, gdy wystąpi zdarzenie, i wykonuje swoje zadania (patrz Co mogę zrobić za pomocą Cloud Functions?). (przykłady przypadków użycia znajdziesz poniżej).
- Otrzymuje obiekt danych, który zawiera zrzut danych przechowywanych w określonym dokumencie.
Aktywowanie funkcji Realtime Database
Twórz nowe funkcje dla zdarzeń Realtime Database za pomocą functions.database. Aby kontrolować, kiedy funkcja jest aktywowana, określ jeden z modułów obsługi zdarzeń i ścieżkę Realtime Database, na której będzie nasłuchiwać zdarzeń.
Ustawianie modułu obsługi zdarzeń
Funkcje umożliwiają obsługę zdarzeń Realtime Database na 2 poziomach szczegółowości. Możesz nasłuchiwać tylko zdarzeń tworzenia, aktualizacji lub usunięcia albo dowolnych zmian w ścieżce. Cloud Functions obsługuje te moduły obsługi zdarzeń w przypadku Realtime Database:
onWrite(), która jest aktywowana, gdy dane są tworzone, aktualizowane lub usuwane w Realtime Database.onCreate(), który jest aktywowany, gdy w Realtime Database zostaną utworzone nowe dane.onUpdate(), który jest aktywowany, gdy dane są aktualizowane w Realtime Database .onDelete(), która jest aktywowana, gdy dane są usuwane z Realtime Database .
Określanie instancji i ścieżki
Aby kontrolować, kiedy i gdzie funkcja ma się uruchamiać, wywołaj ref(path), aby określić ścieżkę, i opcjonalnie określ instancję Realtime Database za pomocą instance('INSTANCE_NAME'). Jeśli nie określisz instancji, funkcja zostanie wdrożona w domyślnej instancji Realtime Database projektu w Firebase. Na przykład:
- Domyślna instancja Realtime Database:
functions.database.ref('/foo/bar') - Instancja o nazwie „my-app-db-2”:
functions.database.instance('my-app-db-2').ref('/foo/bar')
Te metody kierują funkcję do obsługi zapisów w określonej ścieżce w instancji Realtime Database. Specyfikacje ścieżki pasują do wszystkich zapisów, które dotyczą ścieżki, w tym zapisów, które występują w dowolnym miejscu poniżej. Jeśli ustawisz ścieżkę funkcji jako /foo/bar, będzie ona pasować do zdarzeń w obu tych lokalizacjach:
/foo/bar
/foo/bar/baz/really/deep/path
W obu przypadkach Firebase interpretuje, że zdarzenie występuje o /foo/bar, a dane zdarzenia obejmują stare i nowe dane z /foo/bar. Jeśli dane zdarzenia mogą być duże, rozważ użycie wielu funkcji na głębszych ścieżkach zamiast jednej funkcji w pobliżu katalogu głównego bazy danych. Aby uzyskać najlepszą wydajność, żądaj danych tylko na najgłębszym możliwym poziomie.
Możesz określić komponent ścieżki jako symbol wieloznaczny, umieszczając go w nawiasach klamrowych. ref('foo/{bar}') pasuje do dowolnego elementu podrzędnego /foo. Wartości tych komponentów ścieżki wieloznacznika są dostępne w obiekcie EventContext.params funkcji. W tym przykładzie wartość jest dostępna jako context.params.bar.
Ścieżki z symbolami wieloznacznymi mogą pasować do wielu zdarzeń z jednego zapisu. Wstawienie
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
pasuje do ścieżki "/foo/{bar}" dwukrotnie: raz z "hello": "world" i raz z "firebase": "functions".
Obsługa danych zdarzeń
Podczas obsługi zdarzenia Realtime Database zwracany obiekt danych to DataSnapshot.
W przypadku zdarzeń onWrite lub onUpdate pierwszym parametrem jest obiekt Change, który zawiera 2 migawki reprezentujące stan danych przed zdarzeniem wywołującym i po nim. W przypadku zdarzeń onCreate i onDelete zwracany obiekt danych jest migawką utworzonych lub usuniętych danych.
W tym przykładzie funkcja pobiera zrzut dla określonej ścieżki, konwertuje ciąg znaków w tej lokalizacji na wielkie litery i zapisuje zmodyfikowany ciąg znaków w bazie danych:
// 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); });
Uzyskiwanie dostępu do informacji o uwierzytelnianiu użytkowników
Z obiektów EventContext.auth i EventContext.authType możesz uzyskać dostęp do informacji o użytkowniku, który wywołał funkcję, w tym do jego uprawnień. Może to być przydatne do egzekwowania reguł zabezpieczeń, umożliwiając funkcji wykonywanie różnych operacji w zależności od poziomu uprawnień użytkownika:
const functions = require('firebase-functions/v1');
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);
}
});
Możesz też wykorzystać informacje o uwierzytelnianiu użytkownika, aby „podszyć się” pod niego i wykonywać operacje zapisu w jego imieniu. Aby uniknąć problemów z równoczesnością, usuń instancję aplikacji w sposób pokazany poniżej:
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));
});
});
Odczytaj poprzednią wartość
Obiekt Change ma właściwość before, która pozwala sprawdzić, co zostało zapisane w Realtime Database przed zdarzeniem. Właściwość before zwraca obiekt DataSnapshot, w którym wszystkie metody (np. val() i exists()) odwołują się do poprzedniej wartości. Nową wartość możesz odczytać ponownie, używając oryginalnego elementu DataSnapshot lub odczytując właściwość after. Ta właściwość w dowolnym obiekcie Change to kolejny obiekt DataSnapshot reprezentujący stan danych po wystąpieniu zdarzenia.
Na przykład właściwości before można użyć, aby funkcja zamieniała tekst na wielkie litery tylko podczas jego tworzenia:
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);
});