Déclencheurs Realtime Database


Avec Cloud Functions, vous pouvez gérer les événements dans 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 l'instantané de données 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 chemin Realtime Database 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 à ce chemin d'accès.

Vous pouvez déclencher une fonction en réponse à l'écriture, à la création, à la mise à jour ou à la suppression de nœuds de base de données dans Firebase Realtime 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 l'emplacement de la fonction

La distance entre l'emplacement d'une instance Realtime Database et l'emplacement de la fonction peut créer une latence réseau importante. De plus, une incohérence entre les régions peut entraîner un échec du déploiement. Pour éviter ces situations, spécifiez l'emplacement de la fonction afin qu'il corresponde à l'emplacement de l'instance de base de données.

Gérer les événements Realtime Database

Cloud Functions vous permet de gérer les événements Realtime Database à deux niveaux de spécificité. Vous pouvez écouter uniquement les événements d'écriture, de création, de mise à jour ou de suppression, ou bien écouter toute modification apportée à une référence.

Les gestionnaires suivants permettent de répondre aux événements Realtime Database:

Node.js

  • onValueWritten() Déclenché lors de la création, de la mise à jour ou de la suppression de données dans Realtime Database.
  • onValueCreated() Ne se déclenche que lorsque des données sont créées dans Realtime Database.
  • onValueUpdated() Ne se déclenche que lorsque les données sont mises à jour dans Realtime Database.
  • onValueDeleted() Ne se déclenche que lorsque des données sont supprimées dans Realtime Database.

Python

  • on_value_written() Déclenché lors de la création, de la mise à jour ou de la suppression de données dans Realtime Database.
  • on_value_created() Ne se déclenche que lorsque des données sont créées dans Realtime Database.
  • on_value_updated() Ne se déclenche que lorsque les données sont mises à jour dans Realtime Database.
  • on_value_deleted() Ne se déclenche que lorsque des données sont supprimées dans Realtime Database.

Importer les modules requis

Dans le code source de votre fonction, vous devez importer les modules de SDK que vous souhaitez utiliser. Pour cet exemple, vous devez importer les modules HTTP et Realtime Database, ainsi que le module Firebase Admin SDK pour écrire dans Realtime Database.

Node.js

// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onRequest} = require("firebase-functions/v2/https");
const {onValueCreated} = require("firebase-functions/v2/database");
const {logger} = require("firebase-functions");

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp();

Python

# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import db_fn, https_fn

# The Firebase Admin SDK to access the Firebase Realtime Database.
from firebase_admin import initialize_app, db

app = initialize_app()

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

Pour contrôler le moment et le lieu du déclenchement de votre fonction, configurez-la avec un chemin d'accès et éventuellement une instance Realtime Database. Si vous ne spécifiez pas d'instance, la fonction écoute toutes les instances Realtime Database de la région de la fonction. Vous pouvez également spécifier un modèle d'instance Realtime Database à déployer sur un sous-ensemble sélectif d'instances dans la même région.

Exemple :

Node.js

// All Realtime Database instances in default function region us-central1 at path "/user/{uid}"
// There must be at least one Realtime Database present in us-central1.
const onWrittenFunctionDefault = onValueWritten("/user/{uid}", (event) => {
  // …
});

// Instance named "my-app-db-2", at path "/user/{uid}".
// The "my-app-db-2" instance must exist in this region.
const OnWrittenFunctionInstance = onValueWritten(
  {
    ref: "/user/{uid}",
    instance: "my-app-db-2"
    // This example assumes us-central1, but to set location:
    // region: "europe-west1"
  },
  (event) => {
    // …
  }
);

// Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
// There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
const onWrittenFunctionInstance = onValueWritten(
  {
    ref: "/user/{uid=*@gmail.com}",
    instance: "my-app-db-*"
    // This example assumes us-central1, but to set location:
    // region: "europe-west1"
  },
  (event) => {
    // …
  }
);

Python

# All Realtime Database instances in default function region us-central1 at path "/user/{uid}"
# There must be at least one Realtime Database present in us-central1.
@db_fn.on_value_written(r"/user/{uid}")
def onwrittenfunctiondefault(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
@db_fn.on_value_written(
    reference=r"/user/{uid}",
    instance="my-app-db-2",
    # This example assumes us-central1, but to set location:
    # region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

# Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
# There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
@db_fn.on_value_written(
    reference=r"/user/{uid=*@gmail.com}",
    instance="my-app-db-*",
    # This example assumes us-central1, but to set location:
    # region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

Ces paramètres 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 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 que 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.

Caractères génériques et capture

Vous pouvez utiliser {key}, {key=*}, {key=prefix*} et {key=*suffix} pour la capture. *, prefix* et *suffix pour les caractères génériques à un seul segment. Remarque: ** représente les caractères génériques multisegments, qui ne sont pas acceptés par Realtime Database. Consultez Comprendre les formats de chemin d'accès.

Caractère générique de chemin d'accès Vous pouvez spécifier un composant de chemin d'accès en tant que caractère générique:

  • à l'aide de l'astérisque *. Par exemple, foo/* correspond à tous les enfants d'un niveau de la hiérarchie des nœuds sous foo/.
  • En utilisant un segment contenant exactement un astérisque, *. Par exemple, foo/app*-us correspond à tous les segments enfants sous foo/ avec le préfixe app et le suffixe -us.

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

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

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

Capture de chemin. Vous pouvez capturer les correspondances de chemin d'accès dans des variables nommées à utiliser dans le code de votre fonction (par exemple, /user/{uid}, /user/{uid=*-us}).

Les valeurs des variables de capture sont disponibles dans l'objet database.DatabaseEvent.params de votre fonction.

Substitution générique d'instance. Vous pouvez également spécifier un composant d'instance à l'aide de caractères génériques. Un caractère générique d'instance peut comporter un préfixe, un suffixe ou les deux (par exemple, my-app-*-prod).

Référence sur les caractères génériques et la capture

Avec Cloud Functions (2e génération) et Realtime Database, un format peut être utilisé lors de la spécification de ref et instance. Chaque interface de déclencheur propose les options suivantes pour définir le champ d'application d'une fonction:

Spécifier ref Spécifier instance Comportement
Simple (/foo/bar) Ne pas spécifier Définit le gestionnaire de portée sur toutes les instances de la région de fonction.
Simple (/foo/bar) Simple (‘my-new-db') Définit le gestionnaire sur l'instance spécifique de la région de la fonction.
Simple (/foo/bar) Motif (‘inst-prefix*') Définit le gestionnaire de portée sur toutes les instances correspondant au modèle dans la région de la fonction.
Motif (/foo/{bar}) Ne pas spécifier Définit le gestionnaire de portée sur toutes les instances de la région de fonction.
Motif (/foo/{bar}) Simple (‘my-new-db') Définit le gestionnaire sur l'instance spécifique de la région de la fonction.
Motif (/foo/{bar}) Motif (‘inst-prefix*') Définit le gestionnaire de portée sur toutes les instances correspondant au modèle dans la région de la fonction.

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

Lorsqu'un événement Realtime Database se déclenche, il transmet un objet Event à votre fonction de gestionnaire. Cet objet possède une propriété data, qui, pour les événements de création et de suppression, contient un instantané des données créées ou supprimées.

Dans cet exemple, la fonction récupère les données du chemin référencé, convertit la chaîne à cet emplacement en majuscules et écrit cette chaîne modifiée dans la base de données:

Node.js

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
// for all databases in 'us-central1'
exports.makeuppercase = onValueCreated(
    "/messages/{pushId}/original",
    (event) => {
    // Grab the current value of what was written to the Realtime Database.
      const original = event.data.val();
      logger.log("Uppercasing", event.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing
      // asynchronous tasks inside a function, such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the
      // Realtime Database returns a Promise.
      return event.data.ref.parent.child("uppercase").set(uppercase);
    },
);

Python

@db_fn.on_value_created(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[Any]) -> None:
    """Listens for new messages added to /messages/{pushId}/original and
    creates an uppercase version of the message to /messages/{pushId}/uppercase
    """

    # Grab the value that was written to the Realtime Database.
    original = event.data
    if not isinstance(original, str):
        print(f"Not a string: {event.reference}")
        return

    # Use the Admin SDK to set an "uppercase" sibling.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent = db.reference(event.reference).parent
    if parent is None:
        print("Message can't be root node.")
        return
    parent.child("uppercase").set(upper)

Lire la valeur précédente

Pour les événements write ou update, la propriété data 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. L'objet Change possède une propriété before qui vous permet d'inspecter ce qui a été enregistré dans Realtime Database avant l'événement et une propriété after qui représente 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:

Node.js

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

Python

@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase2(event: db_fn.Event[db_fn.Change]) -> None:
    """Listens for new messages added to /messages/{pushId}/original and
    creates an uppercase version of the message to /messages/{pushId}/uppercase
    """

    # Only edit data when it is first created.
    if event.data.before is not None:
        return

    # Exit when the data is deleted.
    if event.data.after is None:
        return

    # Grab the value that was written to the Realtime Database.
    original = event.data.after
    if not hasattr(original, "upper"):
        print(f"Not a string: {event.reference}")
        return

    # Use the Admin SDK to set an "uppercase" sibling.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent = db.reference(event.reference).parent
    if parent is None:
        print("Message can't be root node.")
        return
    parent.child("uppercase").set(upper)