Déclencheurs Realtime Database (1re génération)

Avec Cloud Functions, vous pouvez gérer les événements dans le 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 apporter des modifications Firebase Realtime Database via DataSnapshot ou via le SDK Admin.

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

  1. Attend les modifications apportées à un emplacement Realtime Database spécifique.
  2. Se déclenche lorsqu'un événement se produit et exécute ses tâches (consultez Que puis-je faire avec Cloud Functions? pour obtenir 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 Realtime Database fonction

Créez des fonctions pour les Realtime Database événements avec functions.database. Pour contrôler le moment où la fonction se déclenche, spécifiez l'un des gestionnaires d'événements et spécifiez le Realtime Database chemin d'accès 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 des données sont mises à jour dans Realtime Database .
  • onDelete(), qui se déclenche lorsque des données sont supprimées de Realtime Database .

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

Pour contrôler le moment et le lieu du déclenchement de votre fonction, 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 pas d'instance, la fonction est déployée sur l'instance Realtime Database par défaut pour le projet Firebase. Exemple :

  • Instance par défaut : Realtime Database 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 indiquent à votre fonction de gérer les écritures à 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 se trouvant en dessous. Si le chemin d'accès de votre fonction est défini 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 incluent les anciennes et les nouvelles données se trouvant à /foo/bar. Si les données d'événement risquent d'être volumineuses, envisagez d'utiliser plusieurs fonctions sur des chemins d'accès plus profonds au lieu d'utiliser une seule fonction près 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 à caractère génériques sont disponibles dans l' EventContext.params objet de votre fonction. Dans cet exemple, la valeur est disponible en tant que 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 Realtime Database événement, l'objet de données renvoyé est un DataSnapshot. Pour les événements onWrite ou onUpdate, le premier paramètre est un objet Change qui contient 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 et é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 utilisateur

À partir de EventContext.auth et EventContext.authType, vous pouvez accéder aux informations de l'utilisateur, y compris les autorisations, pour 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 du niveau d'autorisation de l'utilisateur :

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 utilisateur pour "usurper" l'identité d'un utilisateur et effectuer des opérations d'écriture en son nom. Veillez à supprimer l'instance d'application comme indiqué ci-dessous afin d'é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'Change objet comporte une before propriété qui vous permet d'inspecter ce qui a été enregistré dans Realtime Database avant l' événement. 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'original DataSnapshot ou en lisant la after propriété. 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);
    });