Avec Cloud Functions, vous pouvez déployer du code pour gérer les événements déclenchés par des modifications de votre base de données Cloud Firestore. Ce service vous permet d'ajouter facilement des fonctionnalités côté serveur à votre application, sans avoir à gérer vos propres serveurs.
Cloud Functions (2e génération)
Basé sur Cloud Run et Eventarc, Cloud Functions for Firebase (2e génération) vous offre une infrastructure plus puissante, un contrôle avancé des performances et de l'évolutivité, et un contrôle accru sur l'environnement d'exécution des fonctions. Pour en savoir plus sur la 2e génération, consultez la page Cloud Functions pour Firebase (2e génération). Pour en savoir plus sur la 1re génération, consultez Étendre Cloud Firestore avec Cloud Functions.
Cloud Firestore déclencheurs de fonction
Le SDK Cloud Functions for Firebase exporte les déclencheurs d'événements Cloud Firestore suivants pour vous permettre de créer des gestionnaires associés à des événements Cloud Firestore spécifiques:
Node.js
Type d'événement | Déclencheur |
---|---|
onDocumentCreated |
Déclenché lorsqu'un document est écrit pour la première fois. |
onDocumentUpdated |
Déclenché lorsqu'un document existe déjà et qu'une valeur y a été modifiée. |
onDocumentDeleted |
Déclenché lorsqu'un document est supprimé. |
onDocumentWritten |
Déclenché lorsque onDocumentCreated , onDocumentUpdated ou onDocumentDeleted est déclenché. |
onDocumentCreatedWithAuthContext |
onDocumentCreated par des informations d'authentification supplémentaires |
onDocumentWrittenWithAuthContext |
onDocumentWritten avec des informations d'authentification supplémentaires |
onDocumentDeletedWithAuthContext |
onDocumentDeleted avec des informations d'authentification supplémentaires |
onDocumentUpdatedWithAuthContext |
onDocumentUpdated par des informations d'authentification supplémentaires |
Python (bêta)
Type d'événement | Déclencheur |
---|---|
on_document_created |
Déclenché lorsqu'un document est écrit pour la première fois. |
on_document_updated |
Déclenché lorsqu'un document existe déjà et qu'une valeur y a été modifiée. |
on_document_deleted |
Déclenché lorsqu'un document est supprimé. |
on_document_written |
Déclenché lorsque on_document_created , on_document_updated ou on_document_deleted est déclenché. |
on_document_created_with_auth_context |
on_document_created par des informations d'authentification supplémentaires |
on_document_updated_with_auth_context |
on_document_updated avec des informations d'authentification supplémentaires |
on_document_deleted_with_auth_context |
on_document_deleted avec des informations d'authentification supplémentaires |
on_document_written_with_auth_context |
on_document_written avec des informations d'authentification supplémentaires |
Les événements Cloud Firestore ne se déclenchent que lorsque le document est modifié. Une mise à jour d'un document Cloud Firestore où les données restent inchangées (écriture no-op) ne génère pas d'événement de mise à jour ou d'écriture. Il est impossible d'ajouter des événements à des champs spécifiques.
Si vous n'avez pas encore activé de projet dans Cloud Functions for Firebase, consultez la page Premiers pas avec Cloud Functions for Firebase (2e génération) pour en configurer un.
Écrire des fonctions déclenchées par Cloud Firestore
Définir un déclencheur de fonction
Pour définir un déclencheur Cloud Firestore, spécifiez un chemin de document et un type d'événement :
Node.js
import {
onDocumentWritten,
onDocumentCreated,
onDocumentUpdated,
onDocumentDeleted,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.myfunction = onDocumentWritten("my-collection/{docId}", (event) => {
/* ... */
});
Python (preview)
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:
Les chemins de document peuvent faire référence soit à un document spécifique, soit à un format contenant des caractères génériques.
Spécifier un document unique
Si vous souhaitez déclencher un événement chaque fois qu'une modification est constatée sur un document spécifique, vous pouvez utiliser la fonction suivante.
Node.js
import {
onDocumentWritten,
Change,
FirestoreEvent
} from "firebase-functions/v2/firestore";
exports.myfunction = onDocumentWritten("users/marie", (event) => {
// Your code here
});
Python (bêta)
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:
Spécifier un groupe de documents à l'aide de caractères génériques
Si vous souhaitez associer un déclencheur à un groupe de documents, tel que l'ensemble des documents d'une collection spécifique, utilisez un caractère générique {wildcard}
à la place de l'identifiant du document :
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 (bêta)
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
Dans cet exemple, lorsqu'un champ de n'importe quel document de la collection users
est modifié, il correspond au caractère générique userId
.
Si un document de la collection users
comporte des sous-collections et qu'un champ de l'une d'entre elles est modifié, le caractère générique userId
n'est pas déclenché.
Les correspondances de caractères génériques sont extraites du chemin du document et stockées dans le fichier event.params
.
Vous pouvez définir autant de caractères génériques que vous le souhaitez pour remplacer les identifiants explicites de collection ou de document, par exemple :
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 (preview)
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"}
Votre déclencheur doit toujours pointer vers un document, même si vous utilisez un caractère générique.
Par exemple, users/{userId}/{messageCollectionId}
n'est pas valide, car {messageCollectionId}
est une collection. En revanche, users/{userId}/{messageCollectionId}/{messageId}
est valide, car {messageId}
pointe toujours vers un document.
Déclencheurs d'événements
Déclencher une fonction lorsqu'un document est créé
Vous pouvez déclencher une fonction chaque fois qu'un document est créé dans une collection. Cet exemple de fonction se déclenche chaque fois qu'un nouveau profil utilisateur est ajouté:
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 ...
});
Pour obtenir des informations d'authentification supplémentaires, utilisez onDocumentCreatedWithAuthContext
.
Python (bêta)
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 ...
Déclencher une fonction lorsqu'un document est mis à jour
Vous pouvez également déclencher une fonction lors de la mise à jour d'un document. Cet exemple de fonction se déclenche si un utilisateur modifie son 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 ...
});
Pour obtenir des informations d'authentification supplémentaires, utilisez onDocumentUpdatedWithAuthContext
.
Python (bêta)
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 ...
Déclencher une fonction lorsqu'un document est supprimé
Vous pouvez également déclencher une fonction lorsqu'un document est supprimé. Cet exemple de fonction se déclenche lorsqu'un utilisateur supprime son profil utilisateur :
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 ...
});
Pour obtenir des informations d'authentification supplémentaires, utilisez onDocumentDeletedWithAuthContext
.
Python (bêta)
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 ...
Déclencher une fonction pour toute modification apportée à un document
Si le type d'événement déclenché n'a pas d'importance pour vous, vous pouvez écouter toutes les modifications d'un document Cloud Firestore à l'aide du déclencheur d'événement "document écrit". Cet exemple de fonction se déclenche si un utilisateur est créé, mis à jour ou supprimé:
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 ...
});
Pour obtenir des informations d'authentification supplémentaires, utilisez onDocumentWrittenWithAuthContext
.
Python (bêta)
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 ...
Lire et écrire des données
Lorsqu'une fonction est déclenchée, elle fournit un instantané des données associées à l'événement. Vous pouvez utiliser cet instantané pour lire ou écrire dans le document ayant déclenché l'événement, ou utiliser le SDK Firebase Admin pour accéder à d'autres parties de votre base de données.
Données d'événement
Lire les données
Lorsqu'une fonction est déclenchée, vous pouvez obtenir les données d'un document mis à jour ou récupérer la version antérieure à la mise à jour. Vous pouvez obtenir la version antérieure en utilisant event.data.before
, qui contient l'instantané du document avant sa mise à jour.
De même, event.data.after
contient l'état de l'instantané du document après sa mise à jour.
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 (preview)
@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()
Vous pouvez accéder aux propriétés comme vous le feriez pour n'importe quel autre objet. Vous pouvez également utiliser la fonction get
pour accéder à des champs spécifiques :
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 (bêta)
# 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"]
Écrire des données
Chaque appel de fonction est associé à un document spécifique de votre base de données Cloud Firestore. Vous pouvez accéder à ce document dans l'instantané renvoyé à votre fonction.
La référence du document inclut des méthodes telles que update()
, set()
et remove()
afin que vous puissiez modifier le document qui a déclenché la fonction.
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 (bêta)
@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})
Accéder aux informations d'authentification des utilisateurs
Si vous utilisez l'un des types d'événements suivants, vous pouvez accéder aux informations d'authentification utilisateur sur le principal qui a déclenché l'événement. Ces informations s'ajoutent à celles renvoyées dans l'événement de base.
Node.js
onDocumentCreatedWithAuthContext
onDocumentWrittenWithAuthContext
onDocumentDeletedWithAuthContext
onDocumentUpdatedWithAuthContext
Python (preview)
on_document_created_with_auth_context
on_document_updated_with_auth_context
on_document_deleted_with_auth_context
on_document_written_with_auth_context
Pour en savoir plus sur les données disponibles dans le contexte d'authentification, consultez la section Contexte d'authentification. L'exemple suivant montre comment récupérer des informations d'authentification :
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 (preview)
@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
Données situées à l'extérieur de l'événement déclencheur
Cloud Functions s'exécute dans un environnement sécurisé. Ils sont autorisés en tant que compte de service dans votre projet, et vous pouvez effectuer des lectures et des écritures à l'aide du SDK Admin Firebase :
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 (bêta)
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({
# ...
})
Limites
Notez les limites suivantes concernant les déclencheurs Cloud Firestore pour Cloud Functions :
- Cloud Functions (1re génération) nécessite une base de données "(par défaut)" existante dans Firestore en mode natif. La solution n'est pas compatible avec les bases de données nommées Cloud Firestore ni avec le mode Datastore. Veuillez utiliser Cloud Functions (2e génération) pour configurer des événements dans ce cas.
- L'ordre n'est pas garanti. Les modifications rapides peuvent déclencher des appels de fonctions dans un ordre inattendu.
- Bien que les événements soient diffusés une fois au moins, un même événement peut produire plusieurs appels de fonction. Évitez de dépendre de procédés dits "exactement une fois" et écrivez des fonctions idempotentes.
- Cloud Firestore en mode Datastore nécessite Cloud Functions (2e génération). Cloud Functions (1re génération) n'est pas compatible avec le mode Datastore.
- Un déclencheur est associé à une seule base de données. Vous ne pouvez pas créer un déclencheur qui correspond à plusieurs bases de données.
- La suppression d'une base de données ne supprime pas automatiquement les déclencheurs de cette base de données. Le déclencheur cesse de diffuser des événements, mais continue d'exister jusqu'à ce que vous le supprimiez.
- Si un événement correspondant dépasse la taille maximale de requête, il risque de ne pas être distribué à Cloud Functions (1re génération).
- Les événements non distribués en raison de la taille de la requête sont consignés dans les journaux de plate-forme et sont comptabilisés dans l'utilisation des journaux du projet.
- Vous trouverez ces journaux dans l'explorateur de journaux avec le message "Event cannot deliver to Cloud function due to size exceeding the limit for 1st gen..." (l'événement ne peut pas être distribué à la fonction Cloud, car sa taille dépasse la limite pour la 1re génération...) de gravité
error
. Vous trouverez le nom de la fonction dans le champfunctionName
. Si le champreceiveTimestamp
date de moins d'une heure, vous pouvez déduire le contenu réel de l'événement en lisant le document en question avec un instantané avant et après le code temporel. - Pour éviter une telle cadence, vous pouvez :
- Migrer et passer à Cloud Functions (2e génération)
- Réduire la taille du document
- Supprimer l'Cloud Functions en question
- Vous pouvez désactiver la journalisation proprement dite à l'aide d'exclusions, mais notez que les événements mis en cause ne seront toujours pas distribués.