Za pomocą Cloud Functions możesz wdrażać kod do obsługi zdarzeń wywoływanych przez zmiany w bazie danych Cloud Firestore. Umożliwia to łatwe dodawanie do aplikacji funkcji po stronie serwera bez konieczności uruchamiania własnych serwerów.
Cloud Functions (2 generacji)
Dzięki Cloud Run i EventarcCloud Functions for Firebase (2 generacji) zyskujesz sprawniejszą infrastrukturę, zaawansowane zarządzanie wydajnością i skalowalnością oraz większą kontrolę nad środowiskiem wykonawczym funkcji. Więcej informacji o funkcjach drugiej generacji znajdziesz w artykule Cloud Functions for Firebase (2nd gen). Więcej informacji o funkcjach 1. generacji znajdziesz w artykule Rozszerzanie Cloud Firestore za pomocą Cloud Functions.
Wyzwalacze funkcji Cloud Firestore
Pakiet SDK eksportuje te Cloud Functions for Firebasewyzwalacze zdarzeńCloud Firestore, aby umożliwić Ci tworzenie procedur obsługi powiązanych z określonymi Cloud Firestorezdarzeniami:Cloud Firestore
Node.js
Typ zdarzenia | Aktywator |
---|---|
onDocumentCreated |
Wywoływane, gdy dokument jest zapisywany po raz pierwszy. |
onDocumentUpdated |
Wywoływane, gdy dokument już istnieje i zmieni się jego wartość. |
onDocumentDeleted |
Wywoływane po usunięciu dokumentu. |
onDocumentWritten |
Wywoływane, gdy zostanie wywołany element 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
Typ zdarzenia | Aktywator |
---|---|
on_document_created |
Wywoływane, gdy dokument jest zapisywany po raz pierwszy. |
on_document_updated |
Wywoływane, gdy dokument już istnieje i zmieni się jego wartość. |
on_document_deleted |
Wywoływane po usunięciu dokumentu. |
on_document_written |
Wywoływane, gdy zostanie wywołany element 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. |
Cloud Firestore zdarzenia są wywoływane tylko w przypadku zmian w dokumencie. Aktualizacja Cloud Firestore dokumentu, w którym dane nie uległy zmianie (operacja zapisu bez zmian), nie generuje zdarzenia aktualizacji ani zapisu. Nie można dodawać zdarzeń do konkretnych pól.
Jeśli nie masz jeszcze projektu z włączoną usługą Cloud Functions for Firebase, przeczytaj artykuł Wprowadzenie do Cloud Functions for Firebase (2 generacji), aby skonfigurować i przygotować projekt Cloud Functions for Firebase.
Pisanie funkcji wywoływanych przez Cloud Firestore
Definiowanie wyzwalacza funkcji
Aby zdefiniować Cloud Firestore regułę, określ ś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
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.
Określanie pojedynczego dokumentu
Jeśli chcesz wywołać zdarzenie przy każdej zmianie 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
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ślanie grupy dokumentów za pomocą symboli wieloznacznych
Jeśli chcesz dołączyć wyzwalacz do grupy dokumentów, np. do dowolnego dokumentu w określonej kolekcji, użyj znaku {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
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 jakiekolwiek pole w dowolnym dokumencie w users
zostanie zmienione, będzie pasować do symbolu wieloznacznego o nazwie userId
.
Jeśli dokument w users
ma podzbiory i zmienisz pole w jednym z dokumentów podzbiorów, symbol wieloznaczny userId
nie zostanie wywołany.
Wzorce wieloznaczne są wyodrębniane ze ścieżki dokumentu i przechowywane w event.params
.
Możesz zdefiniować dowolną liczbę symboli wieloznacznych, aby zastąpić nimi np. identyfikatory kolekcji lub dokumentów:
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
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"}
Warunek musi zawsze wskazywać dokument, nawet jeśli używasz symbolu wieloznacznego.
Na przykład users/{userId}/{messageCollectionId}
jest nieprawidłowe, ponieważ {messageCollectionId}
jest kolekcją. Jednak users/{userId}/{messageCollectionId}/{messageId}
jest prawidłowy, ponieważ {messageId}
zawsze będzie wskazywać dokument.
Aktywatory zdarzeń
Wywoływanie funkcji po utworzeniu nowego dokumentu
Możesz aktywować funkcję za każdym razem, gdy w kolekcji zostanie utworzony nowy dokument. Ta przykładowa funkcja jest aktywowana 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 ...
});
Dodatkowe informacje o uwierzytelnianiu znajdziesz w sekcji onDocumentCreatedWithAuthContext
.
Python
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 ...
Uruchamianie funkcji po zaktualizowaniu dokumentu
Możesz też uruchomić funkcję, gdy dokument zostanie zaktualizowany. Ta przykładowa funkcja jest wywoływana, 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 ...
});
Dodatkowe informacje o uwierzytelnianiu znajdziesz w sekcji onDocumentUpdatedWithAuthContext
.
Python
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 ...
Uruchamianie funkcji po usunięciu dokumentu
Możesz też uruchomić funkcję, gdy dokument zostanie usunięty. Ten przykład funkcji jest wywoływany, gdy użytkownik usuwa 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 ...
});
Dodatkowe informacje o uwierzytelnianiu znajdziesz w sekcji onDocumentDeletedWithAuthContext
.
Python
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 w przypadku wszystkich zmian w dokumencie
Jeśli nie zależy Ci na typie wywoływanego zdarzenia, możesz nasłuchiwać wszystkich zmian w Cloud Firestoredokumencie za pomocą reguły uruchamiającej zdarzenie „document written” (zapisano dokument). Ta przykładowa funkcja jest wywoływana, 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 ...
});
Dodatkowe informacje o uwierzytelnianiu znajdziesz w sekcji onDocumentWrittenWithAuthContext
.
Python
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
Gdy funkcja zostanie wywołana, udostępni migawkę danych związanych z wydarzeniem. Możesz użyć tego migawki, aby odczytać lub zapisać dokument, który wywołał zdarzenie, albo użyć pakietu Firebase Admin SDK, aby uzyskać dostęp do innych części bazy danych.
Dane zdarzenia
Odczytywanie danych
Gdy funkcja jest wywoływana, możesz chcieć pobrać dane z dokumentu, który został zaktualizowany, lub pobrać dane sprzed aktualizacji. Poprzednie dane możesz uzyskać za pomocą funkcji event.data.before
, która zawiera zrzut 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
@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()
Dostęp do właściwości jest taki sam jak w przypadku innych obiektów. Możesz też użyć funkcji get
, aby uzyskać dostęp do określonych pól:
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
# 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 konkretnym dokumentem w bazie danychCloud Firestore. Dostęp do tego dokumentu możesz uzyskać w migawce zwróconej do funkcji.
Odwołanie do dokumentu zawiera metody takie jak update()
, set()
i remove()
, dzięki czemu możesz modyfikować dokument, który wywołał 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
@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 o uwierzytelnianiu użytkowników
Jeśli używasz jednego z tych typów zdarzeń, możesz uzyskać informacje o uwierzytelnianiu użytkownika dotyczące podmiotu, który wywołał zdarzenie. Te informacje są dodatkowe w stosunku do informacji zwracanych w zdarzeniu podstawowym.
Node.js
onDocumentCreatedWithAuthContext
onDocumentWrittenWithAuthContext
onDocumentDeletedWithAuthContext
onDocumentUpdatedWithAuthContext
Python
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 artykule Kontekst uwierzytelniania. Poniższy przykład pokazuje, jak pobrać informacje o uwierzytelnianiu:
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
@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 spoza zdarzenia wywołującego
Cloud Functions wykonywać w zaufanym środowisku; mają uprawnienia konta usługi w Twoim projekcie, dzięki czemu możesz odczytywać i zapisywać dane za pomocą pakietu Firebase Admin SDK:
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
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
Pamiętaj o tych ograniczeniach dotyczących wyzwalaczy Cloud Firestore dla Cloud Functions:
- Cloud Functions (1 generacji) wymaga istniejącej bazy danych „(default)” w trybie natywnym Firestore. Nie obsługuje Cloud Firestore nazwanych baz danych ani trybu Datastore. W takich przypadkach do konfigurowania zdarzeń używaj urządzenia Cloud Functions (2 generacji).
- Kolejność nie jest gwarantowana. Szybkie zmiany mogą powodować wywoływanie funkcji w nieoczekiwanej kolejności.
- Zdarzenia są dostarczane co najmniej raz, ale jedno zdarzenie może spowodować wielokrotne wywołanie funkcji. Unikaj polegania na mechanizmach przetwarzania dokładnie raz i pisz funkcje idempotentne.
- Cloud Firestore w trybie Datastore wymaga Cloud Functions (2 generacji). Usługa Cloud Functions (1 generacji) nie obsługuje trybu Datastore.
- Aktywator jest powiązany z 1 bazą danych. Nie możesz utworzyć aktywatora, który pasuje do wielu baz danych.
- Usunięcie bazy danych nie powoduje automatycznego usunięcia żadnych wyzwalaczy tej bazy. Wywoływacz przestaje dostarczać zdarzenia, ale nadal istnieje, dopóki go nie usuniesz.
- Jeśli pasujące 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 „Event cannot deliver to
Cloud function due to size exceeding the limit for 1st gen..." o
error
poziomie ważności. Nazwę funkcji znajdziesz w polufunctionName
. Jeśli polereceiveTimestamp
nadal mieści się w zakresie godziny od teraz, możesz wywnioskować rzeczywistą treść wydarzenia, czytając dany dokument z migawką przed i po sygnaturze czasowej. - Aby uniknąć takiej częstotliwości:
- Migracja i uaktualnienie do Cloud Functions (2 generacji)
- Zmniejszanie rozmiaru dokumentu
- Usuń Cloud Functions, o które chodzi.
- Możesz wyłączyć logowanie za pomocą wykluczeń, ale pamiętaj, że problematyczne zdarzenia nadal nie będą dostarczane.