Déclencheurs Cloud Firestore


Avec Cloud Functions, vous pouvez gérer les événements dans Cloud Firestore sans avoir besoin de mettre à jour le code client. Vous pouvez apporter des modifications à Cloud Firestore via l'interface d'instantané de document ou via le SDK Admin .

Dans un cycle de vie typique, une fonction Cloud Firestore effectue les opérations suivantes :

  1. Attend les modifications apportées à un document particulier.
  2. Se déclenche lorsqu'un événement se produit et exécute ses tâches.
  3. Reçoit un objet de données qui contient un instantané des données stockées dans le document spécifié. Pour les événements d'écriture ou de mise à jour, l'objet de données contient deux instantanés qui représentent l'état des données avant et après l'événement déclencheur.

La distance entre l'emplacement de l'instance Firestore et l'emplacement de la fonction peut créer une latence réseau importante. Pour optimiser les performances, pensez à spécifier l' emplacement de la fonction, le cas échéant.

Déclencheurs de fonction Cloud Firestore

Le SDK Cloud Functions pour Firebase exporte un objet functions.firestore qui vous permet de créer des gestionnaires liés à des événements Cloud Firestore spécifiques.

Type d'événement Déclenchement
onCreate Déclenché lorsqu'un document est écrit pour la première fois.
onUpdate Déclenché lorsqu'un document existe déjà et dont une valeur a été modifiée.
onDelete Déclenché lorsqu'un document contenant des données est supprimé.
onWrite Déclenché lorsque onCreate , onUpdate ou onDelete est déclenché.

Si vous n'avez pas encore de projet activé pour Cloud Functions pour Firebase, lisez Premiers pas : écrivez et déployez vos premières fonctions pour configurer et configurer votre projet Cloud Functions pour Firebase.

É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 :

Noeud.js

const functions = require('firebase-functions');

exports.myFunction = functions.firestore
  .document('my-collection/{docId}')
  .onWrite((change, context) => { /* ... */ });

Les chemins d'accès aux documents peuvent faire référence soit à un document spécifique , soit à un modèle générique .

Spécifier un seul document

Si vous souhaitez déclencher un événement pour toute modification apportée à un document spécifique, vous pouvez utiliser la fonction suivante.

Noeud.js

// Listen for any change on document `marie` in collection `users`
exports.myFunctionName = functions.firestore
    .document('users/marie').onWrite((change, context) => {
      // ... Your code here
    });

Spécifier un groupe de documents à l'aide de caractères génériques

Si vous souhaitez attacher un déclencheur à un groupe de documents, tel que n'importe quel document d'une certaine collection, utilisez un {wildcard} à la place de l'ID du document :

Noeud.js

// Listen for changes in all documents in the 'users' collection
exports.useWildcard = functions.firestore
    .document('users/{userId}')
    .onWrite((change, context) => {
      // If we set `/users/marie` to {name: "Marie"} then
      // context.params.userId == "marie"
      // ... and ...
      // change.after.data() == {name: "Marie"}
    });

Dans cet exemple, lorsqu'un champ d'un document dans users est modifié, il correspond à un caractère générique appelé userId .

Si un document dans users a des sous-collections et qu'un champ dans l'un des documents de ces sous-collections est modifié, le caractère générique userId n'est pas déclenché.

Les correspondances génériques sont extraites du chemin du document et stockées dans context.params . Vous pouvez définir autant de caractères génériques que vous le souhaitez pour remplacer les ID de collection ou de document explicites, par exemple :

Noeud.js

// Listen for changes in all documents in the 'users' collection and all subcollections
exports.useMultipleWildcards = functions.firestore
    .document('users/{userId}/{messageCollectionId}/{messageId}')
    .onWrite((change, context) => {
      // If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
      // context.params.userId == "marie";
      // context.params.messageCollectionId == "incoming_messages";
      // context.params.messageId == "134";
      // ... and ...
      // change.after.data() == {body: "Hello"}
    });

Déclencheurs d'événements

Déclencher une fonction lors de la création d'un nouveau document

Vous pouvez déclencher le déclenchement d'une fonction à chaque fois qu'un nouveau document est créé dans une collection en utilisant un gestionnaire onCreate() avec un caractère générique . Cet exemple de fonction appelle createUser chaque fois qu'un nouveau profil utilisateur est ajouté :

Noeud.js

exports.createUser = functions.firestore
    .document('users/{userId}')
    .onCreate((snap, context) => {
      // Get an object representing the document
      // e.g. {'name': 'Marie', 'age': 66}
      const newValue = snap.data();

      // access a particular field as you would any JS property
      const name = newValue.name;

      // perform desired operations ...
    });

Déclencher une fonction lors de la mise à jour d'un document

Vous pouvez également déclencher le déclenchement d'une fonction lorsqu'un document est mis à jour à l'aide de la fonction onUpdate() avec un caractère générique . Cet exemple de fonction appelle updateUser si un utilisateur modifie son profil :

Noeud.js

exports.updateUser = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Get an object representing the document
      // e.g. {'name': 'Marie', 'age': 66}
      const newValue = change.after.data();

      // ...or the previous value before this update
      const previousValue = change.before.data();

      // access a particular field as you would any JS property
      const name = newValue.name;

      // perform desired operations ...
    });

Déclencher une fonction lorsqu'un document est supprimé

Vous pouvez également déclencher une fonction lorsqu'un document est supprimé à l'aide de la fonction onDelete() avec un caractère générique . Cet exemple de fonction appelle deleteUser lorsqu'un utilisateur supprime son profil utilisateur :

Noeud.js

exports.deleteUser = functions.firestore
    .document('users/{userID}')
    .onDelete((snap, context) => {
      // Get an object representing the document prior to deletion
      // e.g. {'name': 'Marie', 'age': 66}
      const deletedValue = snap.data();

      // perform desired operations ...
    });

Déclencher une fonction pour toutes les modifications apportées à un document

Si vous ne vous souciez pas du type d'événement déclenché, vous pouvez écouter toutes les modifications dans un document Cloud Firestore à l'aide de la fonction onWrite() avec un caractère générique . Cet exemple de fonction appelle modifyUser si un utilisateur est créé, mis à jour ou supprimé :

Noeud.js

exports.modifyUser = functions.firestore
    .document('users/{userID}')
    .onWrite((change, context) => {
      // Get an object with the current document value.
      // If the document does not exist, it has been deleted.
      const document = change.after.exists ? change.after.data() : null;

      // Get an object with the previous document value (for update or delete)
      const oldDocument = change.before.data();

      // perform desired operations ...
    });

Lecture et écriture de données

Lorsqu'une fonction est déclenchée, elle fournit un instantané des données liées à l'événement. Vous pouvez utiliser cet instantané pour lire ou écrire dans le document qui a 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

Lecture des données

Lorsqu'une fonction est déclenchée, vous souhaiterez peut-être obtenir les données d'un document qui a été mis à jour ou obtenir les données avant la mise à jour. Vous pouvez obtenir les données antérieures en utilisant change.before.data() , qui contient l'instantané du document avant la mise à jour. De même, change.after.data() contient l'état de l'instantané du document après la mise à jour.

Noeud.js

exports.updateUser2 = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Get an object representing the current document
      const newValue = change.after.data();

      // ...or the previous value before this update
      const previousValue = change.before.data();
    });

Vous pouvez accéder aux propriétés comme vous le feriez dans n’importe quel autre objet. Alternativement, vous pouvez utiliser la fonction get pour accéder à des champs spécifiques :

Noeud.js

// Fetch data using standard accessors
const age = snap.data().age;
const name = snap.data()['name'];

// Fetch data using built in accessor
const experience = snap.get('experience');

Écriture de données

Chaque appel de fonction est associé à un document spécifique dans votre base de données Cloud Firestore. Vous pouvez accéder à ce document en tant que DocumentReference dans la propriété ref de l'instantané renvoyé à votre fonction.

Cette DocumentReference provient du SDK Cloud Firestore Node.js et inclut des méthodes telles que update() , set() et remove() afin que vous puissiez facilement modifier le document qui a déclenché la fonction.

Noeud.js

// Listen for updates to any `user` document.
exports.countNameChanges = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Retrieve the current and previous value
      const data = change.after.data();
      const previousData = change.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 change.after.ref.set({
        name_change_count: count + 1
      }, {merge: true});
    });

Données en dehors de l'événement déclencheur

Les fonctions Cloud s'exécutent dans un environnement fiable, ce qui signifie qu'elles sont autorisées en tant que compte de service sur votre projet. Vous pouvez effectuer des lectures et des écritures à l'aide du SDK Firebase Admin :

Noeud.js

const admin = require('firebase-admin');
admin.initializeApp();

const db = admin.firestore();

exports.writeToFirestore = functions.firestore
  .document('some/doc')
  .onWrite((change, context) => {
    db.doc('some/otherdoc').set({ ... });
  });

Limites

Notez les limitations suivantes pour les déclencheurs Cloud Firestore pour Cloud Functions :

  • La commande n'est pas garantie. Des changements rapides peuvent déclencher des appels de fonctions dans un ordre inattendu.
  • Les événements sont transmis au moins une fois, mais un seul événement peut entraîner plusieurs appels de fonctions. Évitez de dépendre de mécanismes 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) ne prend pas en charge le mode Datastore.
  • Cloud Functions (1re génération) fonctionne uniquement avec la base de données « (par défaut) » et ne prend pas en charge les bases de données nommées Cloud Firestore. Veuillez utiliser Cloud Functions (2e génération) pour configurer des événements pour les bases de données nommées.
  • Un déclencheur est associé à une seule base de données. Vous ne pouvez pas créer un déclencheur correspondant à 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 transmettre les événements mais continue d'exister jusqu'à ce que vous le supprimiez .
  • Si un événement correspondant dépasse la taille maximale de la requête , l'événement risque de ne pas être transmis à Cloud Functions (1re génération).
    • Les événements non livrés en raison de la taille de la demande sont enregistrés dans les journaux de la plateforme et sont pris en compte dans l'utilisation des journaux pour le projet.
    • Vous pouvez trouver ces journaux dans l'explorateur de journaux avec le message « L'événement ne peut pas être transmis à la fonction Cloud en raison d'une taille dépassant la limite pour la 1ère génération... » de gravité de error . Vous pouvez trouver le nom de la fonction sous le champ functionName . Si le champ receiveTimestamp est toujours dans 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 l'horodatage.
    • Pour éviter une telle cadence, vous pouvez :
      • Migrer et mettre à niveau vers Cloud Functions (2e génération)
      • Réduire la taille du document
      • Supprimer les Cloud Functions en question
    • Vous pouvez désactiver la journalisation elle-même à l'aide d'exclusions , mais notez que les événements incriminés ne seront toujours pas transmis.