Déclencheurs Realtime Database


Avec Cloud Functions, vous pouvez gérer les événements de Firebase Realtime Database sans avoir à mettre à jour le code client. Cloud Functions vous permet d'exécuter des opérations Realtime Database avec des droits d'administrateur complets et garantit que chaque modification de Realtime Database est traitée individuellement. Vous pouvez effectuer Firebase Realtime Database modifications via le DataSnapshot ou via le SDK Admin.

Dans un cycle de vie typique, une fonction Firebase Realtime Database effectue les opérations suivantes:

  1. Attend les modifications apportées à un emplacement Realtime Database particulier.
  2. Se déclenche lorsqu'un événement se produit et exécute ses tâches (voir Que puis-je faire avec Cloud Functions ? pour des exemples de cas d'utilisation).
  3. Reçoit un objet de données qui contient un instantané des données stockées dans le document spécifié.

Déclencher une fonction Realtime Database

Créez des fonctions pour les événements Realtime Database avec functions.database. Pour contrôler le moment où la fonction se déclenche, spécifiez l'un des gestionnaires d'événements et le chemin d'accès Realtime Database où elle écoutera les événements.

Définir le gestionnaire d'événements

Cloud Functions vous permet de gérer les événements Realtime Database à deux niveaux de spécificité. Vous pouvez écouter uniquement les événements de création, de mise à jour ou de suppression ou bien écouter toute modification apportée à un chemin d'accès. Cloud Functions est compatible avec les gestionnaires d'événements suivants pour Realtime Database:

  • onWrite(), qui se déclenche lorsque des données sont créées, mises à jour ou supprimées dans Realtime Database.
  • onCreate(), qui se déclenche lorsque de nouvelles données sont créées dans Realtime Database.
  • onUpdate(), qui se déclenche lorsque les données sont mises à jour dans Realtime Database .
  • onDelete(), qui se déclenche lorsque des données sont supprimées de Realtime Database .

Spécifiez l'instance et le chemin d'accès.

Pour contrôler quand et où votre fonction doit se déclencher, appelez ref(path). pour spécifier un chemin d'accès, et éventuellement une instance Realtime Database avec instance('INSTANCE_NAME'). Si vous ne spécifiez une instance, la fonction est déployée sur l'instance Realtime Database par défaut le projet Firebase. Par exemple:

  • Instance Realtime Database par défaut: functions.database.ref('/foo/bar')
  • Instance nommée "my-app-db-2" : functions.database.instance('my-app-db-2').ref('/foo/bar')

Ces méthodes demandent à votre fonction de gérer les écritures selon un certain chemin d'accès dans l'instance Realtime Database. Les spécifications de chemin d'accès correspondent à toutes les écritures qui touchent un chemin d'accès. y compris les écritures qui se produisent n'importe où en dessous. Si vous définissez le chemin d'accès de votre fonction sur /foo/bar, il correspond aux événements de ces deux emplacements :

 /foo/bar
 /foo/bar/baz/really/deep/path

Dans les deux cas, Firebase interprète que l'événement se produit à /foo/bar, et les données d'événement comprennent les anciennes et les nouvelles données à /foo/bar. Si les données d'événement sont volumineuses, envisagez d'utiliser plusieurs fonctions sur des chemins plus profonds plutôt qu'une seule proche de la racine de votre base de données. Pour des performances optimales, demandez uniquement des données au niveau le plus profond possible.

Vous pouvez spécifier un composant de chemin d'accès en tant que caractère générique en l'entourant d'accolades. ref('foo/{bar}') correspond à n'importe quel enfant de /foo. Les valeurs de ces composants de chemin d'accès à caractère générique sont disponibles dans l'objet EventContext.params de votre fonction. Dans cet exemple, la valeur est disponible sous le nom context.params.bar.

Les chemins d'accès contenant des caractères génériques peuvent correspondre à plusieurs événements d'une seule écriture. Une insertion de

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

correspond au chemin d'accès "/foo/{bar}" deux fois : une fois avec "hello": "world" et une autre fois avec "firebase": "functions".

Gérer les données d'événement

Lors de la gestion d'un événement Realtime Database, l'objet de données renvoyé est un DataSnapshot. Pour les événements onWrite ou onUpdate, le premier paramètre est un objet Change contenant deux instantanés représentant l'état des données avant et après l'événement déclencheur. Pour les événements onCreate et onDelete, l'objet de données renvoyé est un instantané des données créées ou supprimées.

Dans cet exemple, la fonction récupère l'instantané du chemin d'accès spécifié, convertit la chaîne à cet emplacement en majuscules, puis écrit cette chaîne modifiée dans la base de données :

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snapshot, context) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = snapshot.val();
      functions.logger.log('Uppercasing', context.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 snapshot.ref.parent.child('uppercase').set(uppercase);
    });

Accéder aux informations d'authentification des utilisateurs

À partir de EventContext.auth et de EventContext.authType, vous pouvez accéder aux informations utilisateur, y compris aux autorisations, de l'utilisateur qui a déclenché une fonction. Cela peut être utile pour appliquer des règles de sécurité, ce qui permet à votre fonction d'effectuer différentes opérations en fonction de l'état niveau d'autorisation:

const functions = require('firebase-functions/v1');
const admin = require('firebase-admin');

exports.simpleDbFunction = functions.database.ref('/path')
    .onCreate((snap, context) => {
      if (context.authType === 'ADMIN') {
        // do something
      } else if (context.authType === 'USER') {
        console.log(snap.val(), 'written by', context.auth.uid);
      }
    });

Vous pouvez également exploiter les informations d'authentification des utilisateurs pour "usurper l'identité" un utilisateur et effectuer des opérations d'écriture pour le compte de l'utilisateur. Veillez à supprimer l'instance d'application comme indiqué ci-dessous pour éviter les problèmes de simultanéité :

exports.impersonateMakeUpperCase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snap, context) => {
      const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
      appOptions.databaseAuthVariableOverride = context.auth;
      const app = admin.initializeApp(appOptions, 'app');
      const uppercase = snap.val().toUpperCase();
      const ref = snap.ref.parent.child('uppercase');

      const deleteApp = () => app.delete().catch(() => null);

      return app.database().ref(ref).set(uppercase).then(res => {
        // Deleting the app is necessary for preventing concurrency leaks
        return deleteApp().then(() => res);
      }).catch(err => {
        return deleteApp().then(() => Promise.reject(err));
      });
    });

Lire la valeur précédente

L'objet Change possède une before qui vous permet d'inspecter ce qui a été enregistré dans Realtime Database avant la . La propriété before renvoie un DataSnapshot où toutes les méthodes (par exemple, val() et exists()) font référence à la valeur précédente. Vous pouvez lire à nouveau la nouvelle valeur en utilisant l'DataSnapshot d'origine ou en lisant la propriété after. Cette propriété sur n'importe quel Change est un autre DataSnapshot représentant l'état des données après l'événement.

Par exemple, la propriété before peut être utilisée pour s'assurer que la fonction ne met en majuscules le texte que lors de sa première création :

exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onWrite((change, context) => {
      // Only edit data when it is first created.
      if (change.before.exists()) {
        return null;
      }
      // Exit when the data is deleted.
      if (!change.after.exists()) {
        return null;
      }
      // Grab the current value of what was written to the Realtime Database.
      const original = change.after.val();
      console.log('Uppercasing', context.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 change.after.ref.parent.child('uppercase').set(uppercase);
    });