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. Zmiany w Firebase Realtime Database możesz wprowadzać za pomocą interfejsu DataSnapshot
lub pakietu Admin SDK.
W ramach typowego cyklu życia funkcja Firebase Realtime Database wykonuje te czynności:
- Czeka na zmiany w konkretnej lokalizacji Realtime Database.
- Aktywuje się, gdy nastąpi zdarzenie, i wykonuje swoje zadania (patrz Co mogę zrobić za pomocą Cloud Functions? (przykłady zastosowań).
- Otrzymuje obiekt danych zawierający zrzut danych przechowywanych w określonym dokumencie.
Aktywowanie funkcji Realtime Database
tworzyć nowe funkcje dla zdarzeń Realtime Database za pomocą functions.database
. Aby kontrolować, kiedy funkcja ma się aktywować, określ jeden z modułów obsługi zdarzeń i ścieżkę Realtime Database, na której będzie ona nasłuchiwać zdarzeń.
Konfigurowanie modułu obsługi zdarzenia
Funkcje umożliwiają obsługę zdarzeń Realtime Database na 2 poziomach szczegółowości: możesz wykrywać tylko zdarzenia tworzenia, aktualizacji lub usuwania albo dowolne zmiany ścieżki. Cloud Functions obsługuje te moduły obsługi zdarzeń w przypadku Realtime Database:
onWrite()
, które jest wywoływane, gdy w tabeli Realtime Database tworzone, aktualizowane lub usuwane są dane.onCreate()
, która jest uruchaniana, gdy w tabeli Realtime Database są tworzone nowe dane.onUpdate()
, które jest wywoływane, gdy dane są aktualizowane w tabeli Realtime Database .onDelete()
, który uruchamia się po usunięciu danych z tabeli Realtime Database .
Określ instancję i ścieżkę
Aby określić, kiedy i gdzie ma być wywoływana funkcja, użyj funkcji ref(path)
, aby podać ścieżkę, i opcjonalnie użyj funkcji Realtime Database, aby podać instancję funkcji instance('INSTANCE_NAME')
. Jeśli nie określisz instancji, funkcja zostanie wdrożona do domyślnej instancji Realtime Database w projekcie Firebase. 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 zapisu w określonym miejscu w obiekcie Realtime Database. Specyfikacje ścieżek pasują do wszystkich operacji zapisu, które dotyczą ścieżki, w tym operacji zapisu, które występują w dowolnym miejscu poniżej. Jeśli ścieżka funkcji jest ustawiona jako /foo/bar
, funkcja dopasowuje zdarzenia w obu tych lokalizacjach:
/foo/bar
/foo/bar/baz/really/deep/path
W obu przypadkach Firebase interpretuje, że zdarzenie występuje w czasie /foo/bar
, a dane zdarzenia obejmują stare i nowe dane w czasie /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 możliwie najgłębszym poziomie.
Możesz określić element ścieżki jako symbol wieloznaczny, otaczając go nawiasami klamrowymi. ref('foo/{bar}')
pasuje do dowolnego elementu podrzędnego elementu /foo
. Wartości tych komponentów ścieżki symbolu wieloznacznego 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. Wstawka
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
pasuje do ścieżki "/foo/{bar}"
dwukrotnie: raz z "hello": "world"
i ponownie z "firebase": "functions"
.
Obsługa danych zdarzenia
Podczas obsługi zdarzenia Realtime Database zwracany jest obiekt danych DataSnapshot
.
W przypadku zdarzeń onWrite
lub onUpdate
pierwszy parametr to obiekt Change
zawierający 2 migawki, które reprezentują stan danych przed i po zdarzeniu wywołującym. W przypadku zdarzeń onCreate
i onDelete
zwracany obiekt danych to zrzut danych utworzonych lub usuniętych.
W tym przykładzie funkcja pobiera zrzut określonych danych w ścieżce, zamienia ciąg znaków w tym miejscu 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); });
Dostęp do informacji o uwierzytelnianiu użytkowników
Z poziomu EventContext.auth
i EventContext.authType
możesz uzyskać informacje o użytkowniku, w tym jego uprawnienia, które wywołały funkcję. Może to być przydatne do egzekwowania zasad bezpieczeństwa, ponieważ pozwala funkcji wykonywać różne operacje 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 uwierzytelniające użytkownika, aby „udawać” tego użytkownika i wykonywać operacje zapisu w jego imieniu. Aby uniknąć problemów z jednoczestronnością, usuń instancję aplikacji w ten sposób:
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));
});
});
Odczytywanie poprzedniej wartości
Obiekt Change
ma właściwość before
, która umożliwia sprawdzenie, co zostało zapisane w obiekcie Realtime Database przed zdarzeniem. Właściwość before
zwraca wartość DataSnapshot
, w której wszystkie metody (np. val()
i exists()
) odwołują się do poprzedniej wartości. Nową wartość możesz odczytać ponownie, używając pierwotnej właściwości DataSnapshot
lub odczytując właściwość after
. Ta właściwość w przypadku dowolnego Change
to kolejna DataSnapshot
, która reprezentuje stan danych po wystąpieniu zdarzenia.
Na przykład za pomocą właściwości before
możesz się upewnić, że funkcja będzie zamieniać tekst na wielkie litery tylko przy pierwszym utworzeniu:
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);
});