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 osobno. Zmiany Firebase Realtime Database możesz wprowadzać za pomocą snapshotu danych lub pakietu Admin SDK.
W ramach typowego cyklu życia funkcja Firebase Realtime Database wykonuje te czynności:
- Czeka na zmiany w danej ścieżce Realtime Database.
- Jest uruchamiany, gdy wystąpi zdarzenie, i wykonuje swoje zadania.
- Otrzymuje obiekt danych zawierający zrzut danych przechowywanych na tej ścieżce.
Funkcję możesz aktywować w odpowiedzi na zapisywanie, tworzenie, aktualizowanie lub usuwanie węzłów bazy danych w Firebase Realtime Database. Aby określić, kiedy ma się aktywować funkcja, podaj jeden z modułów obsługi zdarzeń i ścieżkę Realtime Database, na której będzie ona nasłuchiwać zdarzeń.
Ustawianie lokalizacji funkcji
Odległość między lokalizacją instancji Realtime Database a lokalizacją funkcji może powodować znaczne opóźnienia w sieci. Niezgodność między regionami może też spowodować niepowodzenie wdrożenia. Aby uniknąć takich sytuacji, określ lokalizację funkcji tak, aby odpowiadała lokalizacji instancji bazy danych.
Obsługa zdarzeń Realtime Database
Funkcje umożliwiają obsługę zdarzeń Realtime Database na 2 poziomach szczegółowości: możesz wykrywać tylko zdarzenia zapisu, tworzenia, aktualizacji lub usunięcia albo dowolną zmianę w dowolnym odwołaniu.
Dostępne są te moduły obsługi zdarzeń Realtime Database:
onValueWritten()
Aktywowane, gdy dane są tworzone, aktualizowane lub usuwane w kontekście Realtime Database.onValueCreated()
Aktywowana tylko wtedy, gdy dane są tworzone w Realtime Database.onValueUpdated()
Aktywowane tylko wtedy, gdy dane są aktualizowane w elementach Realtime Database.onValueDeleted()
Wyzwalanie tylko wtedy, gdy dane zostaną usunięte w Realtime Database.
on_value_written()
Aktywowane, gdy dane są tworzone, aktualizowane lub usuwane w kontekście Realtime Database.on_value_created()
Aktywowana tylko wtedy, gdy dane są tworzone w Realtime Database.on_value_updated()
Aktywowane tylko wtedy, gdy dane są aktualizowane w elementach Realtime Database.on_value_deleted()
Wyzwalanie tylko wtedy, gdy dane zostaną usunięte w Realtime Database.
Importowanie wymaganych modułów
W źródle funkcji musisz zaimportować moduły pakietu SDK, których chcesz używać. W tym przykładzie należy zaimportować moduły HTTP i Realtime Database oraz moduł Firebase Admin SDK, aby zapisywać dane w pliku Realtime Database.
// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onRequest} = require("firebase-functions/v2/https");
const {onValueCreated} = require("firebase-functions/v2/database");
const {logger} = require("firebase-functions");
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp();
# 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()
Określ instancję i ścieżkę
Aby określić, kiedy i gdzie ma być uruchamiana funkcja, skonfiguruj ją z ścieżką i opcjonalnie z instancją Realtime Database. Jeśli nie określisz instancji, funkcja będzie nasłuchiwać wszystkich instancji Realtime Database w regionie funkcji. Możesz też określić wzór instancji Realtime Database, aby wdrożyć go w wybranej podgrupie instancji w tym samym regionie.
Przykład:
// 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) => { // … } );
# 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
Te parametry kierują funkcję do obsługi zapisów w określonej ścieżce w instancji 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 niej. 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, zastanów się nad użyciem wielu funkcji na głębszych ścieżkach zamiast jednej funkcji w pobliżu katalogu głównego bazy danych. Aby uzyskać największą wydajność,
wysyłaj żądania tylko na możliwie najgłębszym poziomie.
Używanie symboli wieloznacznych i przechwytywanie
Do przechwytywania możesz użyć aplikacji {key}
, {key=*}
, {key=prefix*}
lub {key=*suffix}
. *
, prefix*
, *suffix
w przypadku zastąpienia znaków w pojedynczym segmencie.
Uwaga: **
oznacza symbole wieloznaczne w wielu segmentach, które nie są obsługiwane przez Realtime Database.
Zobacz artykuł Omówienie wzorów ścieżek.
Znaki wieloznaczne w ścieżkach. Składnik ścieżki możesz określić jako symbol wieloznaczny:
- Używanie gwiazdki,
*
. Na przykładfoo/*
pasuje do wszystkich elementów podrzędnych na 1 poziomie hierarchii węzła poniżej węzłafoo/
. - Używanie segmentu zawierającego dokładnie gwiazdkę,
*
. Na przykładfoo/app*-us
pasuje do dowolnych segmentów podrzędnych poniżejfoo/
z prefiksemapp
i sufiksem-us
.
Ścieżki z symbolami wieloznacznymi mogą pasować do wielu zdarzeń, np. do jednego zapisu. Wstawka
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
pasuje do ścieżki "/foo/*"
dwukrotnie: raz z "hello": "world"
i ponownie z "firebase": "functions"
.
Rejestrowanie ścieżki. Dopasowania ścieżek możesz zapisywać w nazwanych zmiennych, aby używać ich w kodzie funkcji (np. /user/{uid}
, /user/{uid=*-us}
).
Wartości zmiennych przechwytywania są dostępne w obiekcie database.DatabaseEvent.params funkcji.
Użycie symbolu wieloznacznego w przypadku instancji. Możesz też określić instancję komponentu za pomocą symbolu wieloznacznego. Symbol wieloznaczny instancji może mieć prefiks, sufiks lub oba te elementy (np. my-app-*-prod
).
Odwołania do symboli wieloznacznych i przechwytywania
W przypadku Cloud Functions (2 generacji) i Realtime Database można użyć wzorca podczas określania wartości ref
i instance
. Każdy interfejs reguły będzie miał te opcje określania zakresu funkcji:
Określanie ref |
Określanie instance |
Zachowanie |
---|---|---|
Pojedynczy (/foo/bar ) |
Nieokreślone | Ogranicza zakres działania do wszystkich wystąpień w regionie funkcji. |
Pojedynczy (/foo/bar ) |
Pojedynczy (‘my-new-db' ) |
Ogranicza zakres działania do konkretnej instancji w regionie funkcji. |
Pojedynczy (/foo/bar ) |
Wzór (‘inst-prefix*' ) |
Ogranicza zakres działania do wszystkich wystąpień pasujących do wzorca w regionie funkcji. |
Wzór (/foo/{bar} ) |
Nieokreślone | Ogranicza zakres działania do wszystkich wystąpień w regionie funkcji. |
Wzór (/foo/{bar} ) |
Pojedynczy (‘my-new-db' ) |
Ogranicza zakres działania do konkretnej instancji w regionie funkcji. |
Wzór (/foo/{bar} ) |
Wzór (‘inst-prefix*' ) |
Ogranicza zakres działania do wszystkich wystąpień pasujących do wzorca w regionie funkcji. |
Obsługa danych zdarzenia
Gdy zostanie wywołane zdarzenie Realtime Database, przekazuje ono obiekt Event
do funkcji obsługi.
Ten obiekt ma właściwość data
, która w przypadku zdarzeń tworzenia i usuwania zawiera migawkę utworzonych lub usuniętych danych.
W tym przykładzie funkcja pobiera dane dla ścieżki, do której się odwołuje, 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
// 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);
},
);
@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)
Odczyt poprzedniej wartości
W przypadku zdarzeń write
lub update
właściwość data
jest obiektem Change
zawierającym 2 migawki stanu danych przed i po wywołaniu zdarzenia.
Obiekt Change
ma właściwość before
, która umożliwia sprawdzenie, co zostało zapisane w Realtime Database przed zdarzeniem, oraz właściwość after
, która reprezentuje stan danych po zdarzeniu.
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 = 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); });
@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)