Attivatori di Realtime Database

Con Cloud Functions, puoi gestire gli eventi nel Firebase Realtime Database senza dover aggiornare il codice client. Cloud Functions ti consente di eseguire operazioni Realtime Database con privilegi amministrativi completi e garantisce che ogni modifica a Realtime Database venga elaborata singolarmente. Puoi apportare modifiche a Firebase Realtime Database tramite lo snapshot dei dati o tramite l'SDK Admin.

In un ciclo di vita tipico, una funzione Firebase Realtime Database svolge le seguenti operazioni:

  1. Attende le modifiche a un determinato percorso Realtime Database.
  2. Si attiva quando si verifica un evento ed esegue le attività.
  3. Riceve un oggetto dati che contiene uno snapshot dei dati archiviati in quel percorso.

Puoi attivare una funzione in risposta alla scrittura, alla creazione, all'aggiornamento o all'eliminazione di nodi di database in Firebase Realtime Database. Per controllare quando viene attivata la funzione, specifica uno dei gestori di eventi e il percorso Realtime Database in cui verranno rilevati gli eventi.

Impostazione della posizione della funzione

La distanza tra la posizione di un'istanza Realtime Database e la posizione della funzione può creare una latenza di rete significativa. Inoltre, una mancata corrispondenza tra le regioni può causare un errore di deployment. Per evitare queste situazioni, specifica la posizione della funzione in modo che corrisponda alla posizione dell'istanza del database.

Gestione degli eventi Realtime Database

Le funzioni ti consentono di gestire gli eventi Realtime Database a due livelli di specificità; puoi ascoltare solo gli eventi di scrittura, creazione, aggiornamento o eliminazione oppure qualsiasi tipo di modifica a un riferimento.

Sono disponibili i seguenti gestori per rispondere agli eventi Realtime Database:

Node.js

  • onValueWritten() Attivato quando i dati vengono creati, aggiornati o eliminati in Realtime Database.
  • onValueCreated() Attivato solo quando i dati vengono creati in Realtime Database.
  • onValueUpdated() Attivato solo quando i dati vengono aggiornati in Realtime Database.
  • onValueDeleted() Attivato solo quando i dati vengono eliminati in Realtime Database.

Python

  • on_value_written() Attivato quando i dati vengono creati, aggiornati o eliminati in Realtime Database.
  • on_value_created() Attivato solo quando i dati vengono creati in Realtime Database.
  • on_value_updated() Attivato solo quando i dati vengono aggiornati in Realtime Database.
  • on_value_deleted() Attivato solo quando i dati vengono eliminati in Realtime Database.

Importa i moduli richiesti

Nel codice sorgente della funzione, devi importare i moduli SDK che vuoi utilizzare. Per questo esempio, è necessario importare i moduli HTTP e Realtime Database insieme al modulo Firebase Admin SDK per scrivere in 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()

Specifica l'istanza e il percorso

Per controllare quando e dove deve essere attivata la funzione, configura la funzione con un percorso e, facoltativamente, un'istanza Realtime Database. Se non specifichi un'istanza, la funzione ascolta tutte le istanze Realtime Database nella regione della funzione. Puoi anche specificare un pattern di istanza Realtime Database per il deployment in un sottoinsieme selettivo di istanze nella stessa regione.

Ad esempio:

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

Questi parametri indirizzano la funzione per gestire le scritture in un determinato percorso all'interno dell'istanza Realtime Database.

Le specifiche del percorso corrispondono a tutte le scritture che interessano un percorso, incluse quelle che si verificano in qualsiasi punto sottostante. Se imposti il percorso della funzione come /foo/bar, corrisponde agli eventi in entrambe queste posizioni:

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

In entrambi i casi, Firebase interpreta che l'evento si verifica alle ore /foo/bar, e i dati dell'evento includono i dati vecchi e nuovi alle ore /foo/bar. Se i dati degli eventi potrebbero essere di grandi dimensioni, valuta la possibilità di utilizzare più funzioni in percorsi più profondi anziché una singola funzione vicino alla radice del database. Per ottenere le migliori prestazioni, richiedi i dati solo al livello più profondo possibile.

Utilizzo di caratteri jolly e acquisizione

Puoi utilizzare {key}, {key=*}, {key=prefix*}, {key=*suffix} per l'acquisizione. *, prefix*, *suffix per l'utilizzo di caratteri jolly a segmento singolo. Nota: ** rappresenta l'utilizzo di caratteri jolly in più segmenti, che Realtime Database non supporta. Consulta Comprendere i pattern dei percorsi.

Utilizzo di caratteri jolly nel percorso. Puoi specificare un componente del percorso come carattere jolly:

  • Utilizzo dell'asterisco, *. Ad esempio, foo/* corrisponde a tutti i nodi secondari di un livello della gerarchia dei nodi sottostante foo/.
  • Utilizzo di un segmento contenente esattamente l'asterisco, *. Ad esempio, foo/app*-us corrisponde a tutti i segmenti secondari riportati di seguito foo/ con il prefisso app e il suffisso -us.

I percorsi con caratteri jolly possono corrispondere a più eventi, ad esempio, da una singola scrittura. Un inserto di

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

corrisponde due volte al percorso "/foo/*": una volta con "hello": "world" e una volta con "firebase": "functions".

Acquisizione del percorso. Puoi acquisire le corrispondenze del percorso in variabili denominate da utilizzare nel codice della funzione (ad es. /user/{uid}, /user/{uid=*-us}).

I valori delle variabili di acquisizione sono disponibili all'interno dell'oggetto database.DatabaseEvent.params della funzione.

Utilizzo di caratteri jolly per le istanze. Puoi anche specificare un componente dell'istanza utilizzando i caratteri jolly. Un carattere jolly dell'istanza può avere un prefisso, un suffisso o entrambi (ad es. my-app-*-prod).

Riferimento per caratteri jolly e acquisizione

Con Cloud Functions (2ª gen.) e Realtime Database, è possibile utilizzare un pattern quando si specificano ref e instance. Ogni interfaccia di trigger avrà le seguenti opzioni per definire l'ambito di una funzione:

Specificare ref Specificare instance Comportamento
Singolo (/foo/bar) Non specificare Gestore degli ambiti per tutte le istanze nella regione della funzione.
Singolo (/foo/bar) Singolo (‘my-new-db') Gestore degli ambiti all'istanza specifica nella regione della funzione.
Singolo (/foo/bar) Pattern (‘inst-prefix*') Gestore degli ambiti per tutte le istanze che corrispondono al pattern nella regione della funzione.
Pattern (/foo/{bar}) Non specificare Gestore degli ambiti per tutte le istanze nella regione della funzione.
Pattern (/foo/{bar}) Singolo (‘my-new-db') Gestore degli ambiti all'istanza specifica nella regione della funzione.
Pattern (/foo/{bar}) Pattern (‘inst-prefix*') Gestore degli ambiti per tutte le istanze che corrispondono al pattern nella regione della funzione.

Gestire i dati evento

Quando viene attivato un evento Realtime Database, viene passato un oggetto Event alla funzione di gestione. Questo oggetto ha una proprietà data che, per gli eventi di creazione ed eliminazione, contiene uno snapshot dei dati creati o eliminati.

In questo esempio, la funzione recupera i dati per il percorso di riferimento, converte la stringa in quella posizione in maiuscolo e scrive la stringa modificata nel database:

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)

Leggere il valore precedente

Per gli eventi write o update, la proprietà data è un oggetto Change che contiene due snapshot che rappresentano lo stato dei dati prima e dopo l'evento di attivazione. L'oggetto Change ha una proprietà before che ti consente di esaminare ciò che è stato salvato in Realtime Database prima dell'evento e una proprietà after che rappresenta lo stato dei dati dopo l'evento.

Ad esempio, la proprietà before può essere utilizzata per assicurarsi che la funzione metta in maiuscolo il testo solo al momento della creazione:

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)