Dzięki Cloud Functions możesz wdrożyć kod do obsługi zdarzeń wywoływanych przez zmiany w bazie danych Cloud Firestore. Dzięki temu możesz łatwo dodać do aplikacji funkcje po stronie serwera bez konieczności posiadania własnych serwerów.
Cloud Functions (2 generacji)
Dzięki wykorzystaniu Cloud Run i Eventarc usługa Cloud Functions dla Firebase (2 generacji) zapewnia bardziej zaawansowaną infrastrukturę, zaawansowaną kontrolę nad wydajnością i skalowalnością oraz większą kontrolę nad środowiskiem wykonawczym funkcji. Więcej informacji o 2 generacji znajdziesz w artykule Cloud Functions dla Firebase (2 generacji). Więcej informacji o 1 generacji znajdziesz w artykule Przedłużanie Cloud Firestore za pomocą Cloud Functions.
Aktywatory funkcji Cloud Firestore
Pakiet SDK Cloud Functions dla Firebase eksportuje te aktywatory zdarzeń Cloud Firestore, aby umożliwić Ci utworzenie modułów obsługi powiązanych z określonymi zdarzeniami w Cloud Firestore:
Node.js
Typ zdarzenia | Aktywator |
---|---|
onDocumentCreated |
Wywoływane, gdy dokument jest zapisany po raz pierwszy. |
onDocumentUpdated |
Wywoływane, gdy dokument już istnieje, ale jego wartość uległa zmianie. |
onDocumentDeleted |
Wywoływane po usunięciu dokumentu. |
onDocumentWritten |
Wywoływane po wywołaniu funkcji onDocumentCreated , onDocumentUpdated lub onDocumentDeleted . |
onDocumentCreatedWithAuthContext |
onDocumentCreated z dodatkowymi informacjami uwierzytelniającymi |
onDocumentWrittenWithAuthContext |
onDocumentWritten z dodatkowymi informacjami uwierzytelniającymi |
onDocumentDeletedWithAuthContext |
onDocumentDeleted z dodatkowymi informacjami uwierzytelniającymi |
onDocumentUpdatedWithAuthContext |
onDocumentUpdated z dodatkowymi informacjami uwierzytelniającymi |
Python (wersja testowa)
Typ zdarzenia | Aktywator |
---|---|
on_document_created |
Wywoływane, gdy dokument jest zapisany po raz pierwszy. |
on_document_updated |
Wywoływane, gdy dokument już istnieje, ale jego wartość uległa zmianie. |
on_document_deleted |
Wywoływane po usunięciu dokumentu. |
on_document_written |
Wywoływane po wywołaniu funkcji on_document_created , on_document_updated lub on_document_deleted . |
on_document_created_with_auth_context |
on_document_created z dodatkowymi informacjami uwierzytelniającymi |
on_document_updated_with_auth_context |
on_document_updated z dodatkowymi informacjami uwierzytelniającymi |
on_document_deleted_with_auth_context |
on_document_deleted z dodatkowymi informacjami uwierzytelniającymi |
on_document_written_with_auth_context |
on_document_written z dodatkowymi informacjami uwierzytelniającymi |
Zdarzenia Cloud Firestore są aktywowane tylko przy zmianach w dokumencie. Aktualizacja dokumentu Cloud Firestore, w którym dane nie uległy zmianie (zapis w trybie bezobsługowym), nie spowoduje wygenerowania zdarzenia aktualizacji ani zapisu. Nie można dodawać zdarzeń do określonych pól.
Jeśli nie masz jeszcze projektu, w którym włączono Cloud Functions dla Firebase, przeczytaj artykuł Pierwsze kroki z Cloud Functions dla Firebase (2 generacji), aby skonfigurować swój projekt Cloud Functions dla Firebase.
Jak pisać funkcje aktywowane przez Cloud Firestore
Zdefiniuj aktywator funkcji
Aby zdefiniować aktywator Cloud Firestore, podaj ścieżkę dokumentu i typ zdarzenia:
Node.js
import {
onDocumentWritten,
onDocumentCreated,
onDocumentUpdated,
onDocumentDeleted,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.myfunction = onDocumentWritten("my-collection/{docId}", (event) => {
/* ... */
});
Python (wersja testowa)
from firebase_functions.firestore_fn import (
on_document_created,
on_document_deleted,
on_document_updated,
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:
Ścieżki dokumentów mogą odwoływać się do konkretnego dokumentu lub wzorca z symbolami wieloznacznymi.
Wskaż pojedynczy dokument
Jeśli chcesz wywoływać zdarzenie w przypadku dowolnej zmiany w określonym dokumencie, możesz użyć tej funkcji.
Node.js
import {
onDocumentWritten,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.myfunction = onDocumentWritten("users/marie", (event) => {
// Your code here
});
Python (wersja testowa)
from firebase_functions.firestore_fn import (
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_written(document="users/marie")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
Określ grupę dokumentów przy użyciu symboli wieloznacznych
Jeśli chcesz dołączyć regułę do grupy dokumentów, na przykład dowolnego dokumentu w określonej kolekcji, użyj {wildcard}
zamiast identyfikatora dokumentu:
Node.js
import {
onDocumentWritten,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.myfunction = onDocumentWritten("users/{userId}", (event) => {
// If we set `/users/marie` to {name: "Marie"} then
// event.params.userId == "marie"
// ... and ...
// event.data.after.data() == {name: "Marie"}
});
Python (wersja testowa)
from firebase_functions.firestore_fn import (
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# If we set `/users/marie` to {name: "Marie"} then
event.params["userId"] == "marie" # True
# ... and ...
event.data.after.to_dict() == {"name": "Marie"} # True
W tym przykładzie, gdy zmieni się dowolne pole w dowolnym dokumencie w users
, odpowiada ono symbolowi wieloznacznym o nazwie userId
.
Jeśli dokument w kolekcji users
zawiera kolekcje podrzędne, a pole w jednym z takich dokumentów ulegnie zmianie, symbol wieloznaczny userId
nie zostanie wywołany.
Dopasowania zawierające symbole wieloznaczne są wyodrębniane ze ścieżki dokumentu i przechowywane w elemencie event.params
.
Możesz zdefiniować dowolną liczbę symboli wieloznacznych, aby zastąpić jawne identyfikatory kolekcji lub dokumentów, na przykład:
Node.js
import {
onDocumentWritten,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.myfunction = onDocumentWritten("users/{userId}/{messageCollectionId}/{messageId}", (event) => {
// If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
// event.params.userId == "marie";
// event.params.messageCollectionId == "incoming_messages";
// event.params.messageId == "134";
// ... and ...
// event.data.after.data() == {body: "Hello"}
});
Python (wersja testowa)
from firebase_functions.firestore_fn import (
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_written(document="users/{userId}/{messageCollectionId}/{messageId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
event.params["userId"] == "marie" # True
event.params["messageCollectionId"] == "incoming_messages" # True
event.params["messageId"] == "134" # True
# ... and ...
event.data.after.to_dict() == {"body": "Hello"}
Wyzwalacz musi zawsze wskazywać dokument, nawet jeśli używasz symbolu wieloznacznego.
Na przykład users/{userId}/{messageCollectionId}
nie jest prawidłowy, ponieważ {messageCollectionId}
jest zbiorem. Parametr users/{userId}/{messageCollectionId}/{messageId}
jest jednak prawidłowy, ponieważ {messageId}
zawsze wskazuje dokument.
Wyzwalacze zdarzeń
Aktywuj funkcję podczas tworzenia nowego dokumentu
Możesz aktywować tę funkcję za każdym razem, gdy w kolekcji zostanie utworzony nowy dokument. Ta przykładowa funkcja jest wyzwalana za każdym razem, gdy dodawany jest nowy profil użytkownika:
Node.js
import {
onDocumentCreated,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.createuser = onDocumentCreated("users/{userId}", (event) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const snapshot = event.data;
if (!snapshot) {
console.log("No data associated with the event");
return;
}
const data = snapshot.data();
// access a particular field as you would any JS property
const name = data.name;
// perform more operations ...
});
Aby uzyskać dodatkowe informacje dotyczące uwierzytelniania, użyj onDocumentCreatedWithAuthContext
.
Python (wersja testowa)
from firebase_functions.firestore_fn import (
on_document_created,
Event,
DocumentSnapshot,
)
@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:
# Get a dictionary representing the document
# e.g. {'name': 'Marie', 'age': 66}
new_value = event.data.to_dict()
# Access a particular field as you would any dictionary
name = new_value["name"]
# Perform more operations ...
Wywoływanie funkcji po zaktualizowaniu dokumentu
Możesz też wyzwalać tę funkcję po zaktualizowaniu dokumentu. Ta przykładowa funkcja jest uruchamiana, gdy użytkownik zmieni swój profil:
Node.js
import {
onDocumentUpdated,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.updateuser = onDocumentUpdated("users/{userId}", (event) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = event.data.after.data();
// access a particular field as you would any JS property
const name = newValue.name;
// perform more operations ...
});
Aby uzyskać dodatkowe informacje dotyczące uwierzytelniania, użyj onDocumentUpdatedWithAuthContext
.
Python (wersja testowa)
from firebase_functions.firestore_fn import (
on_document_updated,
Event,
Change,
DocumentSnapshot,
)
@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# Get a dictionary representing the document
# e.g. {'name': 'Marie', 'age': 66}
new_value = event.data.after.to_dict()
# Access a particular field as you would any dictionary
name = new_value["name"]
# Perform more operations ...
Aktywowanie funkcji po usunięciu dokumentu
Możesz też uruchomić tę funkcję po usunięciu dokumentu. Ta przykładowa funkcja uruchamia się, gdy użytkownik usunie swój profil:
Node.js
import {
onDocumentDeleted,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.deleteuser = onDocumentDeleted("users/{userId}", (event) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const snap = event.data;
const data = snap.data();
// perform more operations ...
});
Aby uzyskać dodatkowe informacje dotyczące uwierzytelniania, użyj onDocumentDeletedWithAuthContext
.
Python (wersja testowa)
from firebase_functions.firestore_fn import (
on_document_deleted,
Event,
DocumentSnapshot,
)
@on_document_deleted(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot|None]) -> None:
# Perform more operations ...
Aktywowanie funkcji przy wszystkich zmianach w dokumencie
Jeśli typ wywoływanego zdarzenia nie jest dla Ciebie ważny, możesz nasłuchiwać wszystkich zmian w dokumencie Cloud Firestore za pomocą aktywatora zdarzenia „zapisany dokument”. Ta przykładowa funkcja uruchamia się, gdy użytkownik zostanie utworzony, zaktualizowany lub usunięty:
Node.js
import {
onDocumentWritten,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.modifyuser = onDocumentWritten("users/{userId}", (event) => {
// Get an object with the current document values.
// If the document does not exist, it was deleted
const document = event.data.after.data();
// Get an object with the previous document values
const previousValues = event.data.before.data();
// perform more operations ...
});
Aby uzyskać dodatkowe informacje dotyczące uwierzytelniania, użyj onDocumentWrittenWithAuthContext
.
Python (wersja testowa)
from firebase_functions.firestore_fn import (
on_document_written,
Event,
Change,
DocumentSnapshot,
)
@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
# Get an object with the current document values.
# If the document does not exist, it was deleted.
document = (event.data.after.to_dict()
if event.data.after is not None else None)
# Get an object with the previous document values.
# If the document does not exist, it was newly created.
previous_values = (event.data.before.to_dict()
if event.data.before is not None else None)
# Perform more operations ...
Odczytywanie i zapisywanie danych
Po aktywowaniu funkcji udostępnia zrzut danych związanych ze zdarzeniem. Możesz użyć tego zrzutu, aby odczytywać dokument, który wywołał zdarzenie, lub w nim zapisywać. Możesz też skorzystać z pakietu Firebase Admin SDK, aby uzyskać dostęp do innych części swojej bazy danych.
Dane zdarzenia
Odczytywanie danych
Po wywołaniu funkcji możesz chcieć pobrać dane ze zaktualizowanego dokumentu lub pobrać dane przed aktualizacją. Wcześniejsze dane możesz uzyskać, używając parametru event.data.before
, który zawiera migawkę dokumentu sprzed aktualizacji.
Podobnie event.data.after
zawiera stan zrzutu dokumentu po aktualizacji.
Node.js
exports.updateuser2 = onDocumentUpdated("users/{userId}", (event) => {
// Get an object with the current document values.
// If the document does not exist, it was deleted
const newValues = event.data.after.data();
// Get an object with the previous document values
const previousValues = event.data.before.data();
});
Python (wersja testowa)
@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# Get an object with the current document values.
new_value = event.data.after.to_dict()
# Get an object with the previous document values.
prev_value = event.data.before.to_dict()
Możesz uzyskać dostęp do właściwości tak samo jak w przypadku każdego innego obiektu. Aby uzyskać dostęp do określonych pól, możesz też użyć funkcji get
:
Node.js
// Fetch data using standard accessors
const age = event.data.after.data().age;
const name = event.data.after.data()['name'];
// Fetch data using built in accessor
const experience = event.data.after.data.get('experience');
Python (wersja testowa)
# Get the value of a single document field.
age = event.data.after.get("age")
# Convert the document to a dictionary.
age = event.data.after.to_dict()["age"]
Zapisywanie danych
Każde wywołanie funkcji jest powiązane z określonym dokumentem w bazie danych Cloud Firestore. Możesz uzyskać dostęp do tego dokumentu w zrzucie zwróconym do funkcji.
Odniesienie do dokumentu zawiera metody takie jak update()
, set()
i remove()
, dzięki czemu możesz zmodyfikować dokument, który uruchomił funkcję.
Node.js
import { onDocumentUpdated } from "firebase-functions/v2/firestore";
exports.countnamechanges = onDocumentUpdated('users/{userId}', (event) => {
// Retrieve the current and previous value
const data = event.data.after.data();
const previousData = event.data.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 data.after.ref.set({
name_change_count: count + 1
}, {merge: true});
});
Python (wersja testowa)
@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# Get the current and previous document values.
new_value = event.data.after
prev_value = event.data.before
# We'll only update if the name has changed.
# This is crucial to prevent infinite loops.
if new_value.get("name") == prev_value.get("name"):
return
# Retrieve the current count of name changes
count = new_value.to_dict().get("name_change_count", 0)
# Update the count
new_value.reference.update({"name_change_count": count + 1})
Uzyskiwanie dostępu do informacji uwierzytelniających użytkowników
Jeśli używasz jednego z podanych niżej typów zdarzeń, masz dostęp do informacji uwierzytelniających użytkownika dotyczących podmiotu zabezpieczeń, który wywołał zdarzenie. Są one uzupełnieniem informacji zwracanych w zdarzeniu podstawowym.
Node.js
onDocumentCreatedWithAuthContext
onDocumentWrittenWithAuthContext
onDocumentDeletedWithAuthContext
onDocumentUpdatedWithAuthContext
Python (wersja testowa)
on_document_created_with_auth_context
on_document_updated_with_auth_context
on_document_deleted_with_auth_context
on_document_written_with_auth_context
Informacje o danych dostępnych w kontekście uwierzytelniania znajdziesz w sekcji Kontekst uwierzytelniania. Ten przykład pokazuje, jak pobrać informacje uwierzytelniające:
Node.js
import { onDocumentWrittenWithAuthContext } from "firebase-functions/v2/firestore"
exports.syncUser = onDocumentWrittenWithAuthContext("users/{userId}", (event) => {
const snapshot = event.data.after;
if (!snapshot) {
console.log("No data associated with the event");
return;
}
const data = snapshot.data();
// retrieve auth context from event
const { authType, authId } = event;
let verified = false;
if (authType === "system") {
// system-generated users are automatically verified
verified = true;
} else if (authType === "unknown" || authType === "unauthenticated") {
// admin users from a specific domain are verified
if (authId.endsWith("@example.com")) {
verified = true;
}
}
return data.after.ref.set({
created_by: authId,
verified,
}, {merge: true});
});
Python (wersja testowa)
@on_document_updated_with_auth_context(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
# Get the current and previous document values.
new_value = event.data.after
prev_value = event.data.before
# Get the auth context from the event
user_auth_type = event.auth_type
user_auth_id = event.auth_id
Dane poza zdarzeniem aktywującym
Funkcje w Cloud Functions są wykonywane w zaufanym środowisku. Są one autoryzowane jako konto usługi w projekcie, a Ty możesz wykonywać odczyty i zapisy za pomocą pakietu SDK Firebase Admin:
Node.js
const { initializeApp } = require('firebase-admin/app');
const { getFirestore, Timestamp, FieldValue } = require('firebase-admin/firestore');
initializeApp();
const db = getFirestore();
exports.writetofirestore = onDocumentWritten("some/doc", (event) => {
db.doc('some/otherdoc').set({ ... });
});
exports.writetofirestore = onDocumentWritten('users/{userId}', (event) => {
db.doc('some/otherdoc').set({
// Update otherdoc
});
});
Python (wersja testowa)
from firebase_admin import firestore, initialize_app
import google.cloud.firestore
initialize_app()
@on_document_written(document="some/doc")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
firestore_client: google.cloud.firestore.Client = firestore.client()
firestore_client.document("another/doc").set({
# ...
})
Ograniczenia
Zwróć uwagę na te ograniczenia aktywatorów Cloud Firestore dla Cloud Functions:
- Cloud Functions (1 generacji) wymaga utworzenia istniejącej „(domyślnej)” bazy danych w trybie natywnym Firestore. Nie obsługuje nazwanych baz danych Cloud Firestore ani trybu Datastore. W takich przypadkach używaj Cloud Functions (2 generacji), aby konfigurować zdarzenia.
- Zamówienie nie jest gwarantowane. Szybkie zmiany mogą aktywować wywołania funkcji w nieoczekiwanej kolejności.
- Zdarzenia są dostarczane co najmniej raz, ale pojedyncze zdarzenie może powodować wiele wywołań funkcji. Unikaj polegania na mechanice „dokładnie raz” i zapisz 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żna utworzyć aktywatora pasującego do wielu baz danych.
- Usunięcie bazy danych nie powoduje automatycznego usunięcia żadnych jej aktywatorów. Reguła przestanie dostarczać zdarzenia, ale będzie istnieć, dopóki jej 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 uwzględniane w dzienniku projektu.
- Te logi znajdziesz w eksploratorze logów z komunikatem „Zdarzenie nie może dostarczyć do funkcji w Cloud Functions z powodu przekroczenia limitu dla 1 generacji...” o poziomie ważności
error
. Nazwę funkcji znajdziesz pod polemfunctionName
. Jeśli zawartość polareceiveTimestamp
będzie jeszcze w ciągu godziny, możesz wywnioskować faktyczną treść zdarzenia, odczytując dany dokument z migawką przed sygnaturą czasową i po niej. - Aby tego uniknąć:
- Migracja i przejście na Cloud Functions (2 generacji)
- Zmniejsz rozmiar dokumentu
- Usuń te funkcje w Cloud Functions
- Możesz samodzielnie wyłączyć logowanie przy użyciu wykluczeń, ale pamiętaj, że zdarzenia naruszające zasady nadal nie będą dostarczane.