Con Cloud Functions, puoi gestire gli eventi in
Firebase Realtime Database senza dover aggiornare il codice client.
Cloud Functions consente di eseguire operazioni su Realtime Database con privilegi amministrativi completi e garantisce che ogni modifica a Realtime Database venga elaborata singolarmente. Puoi apportare Firebase Realtime Database modifiche tramite
DataSnapshot
o tramite l'SDK Admin.
In un ciclo di vita tipico, una Firebase Realtime Database funzione esegue queste operazioni:
- Attende le modifiche a una determinata posizione Realtime Database.
- Si attiva quando si verifica un evento ed esegue le attività (vedi Cosa posso fare con Cloud Functions? per esempi di casi d'uso).
- Riceve un oggetto dati che contiene uno snapshot dei dati archiviati nel documento specificato.
Attivare una funzione Realtime Database
Crea nuove funzioni per gli eventi Realtime Database
con functions.database. Per
controllare quando viene attivata la funzione, specifica uno dei gestori di eventi e
specifica il percorso Realtime Database in cui ascolterà gli eventi.
Impostare il gestore di eventi
Functions ti consente di gestire gli eventi Realtime Database a due livelli di specificità. Puoi ascoltare in modo specifico solo gli eventi di creazione, aggiornamento, o eliminazione oppure ascoltare qualsiasi tipo di modifica a un percorso. Cloud Functions supporta i seguenti gestori di eventi per Realtime Database:
onWrite(), che si attiva quando i dati vengono creati, aggiornati o eliminati in Realtime Database.onCreate(), che si attiva quando vengono creati nuovi dati in Realtime Database.onUpdate(), che si attiva quando i dati vengono aggiornati in Realtime Database .onDelete(), che si attiva quando i dati vengono eliminati da Realtime Database .
Specificare l'istanza e il percorso
Per controllare quando e dove deve essere attivata la funzione, chiama ref(path)
per specificare un percorso e, facoltativamente, specifica un'istanza Realtime Databasecon instance('INSTANCE_NAME'). Se non
specifichi un'istanza, la funzione viene eseguita il deployment nell'istanza Realtime Database predefinita per
il progetto Firebase. Ad esempio:
- Istanza Realtime Database predefinita:
functions.database.ref('/foo/bar') - Istanza denominata "my-app-db-2":
functions.database.instance('my-app-db-2').ref('/foo/bar')
Questi metodi indirizzano la funzione alla gestione delle scritture in un determinato percorso all'interno di
Realtime Databaseistanza. Le specifiche del percorso corrispondono a tutte le scritture che interessano un percorso, incluse quelle che si verificano in qualsiasi punto sottostante. Se come percorso della funzione imposti /foo/bar, corrisponde agli eventi in entrambe queste posizioni:
/foo/bar
/foo/bar/baz/really/deep/path
In entrambi i casi, Firebase interpreta l'evento come avvenuto in /foo/bar e i dati sugli eventi includono i dati precedenti e quelli nuovi in /foo/bar. Se è possibile che i dati sugli eventi siano 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 prestazioni migliori, richiedi i dati al livello più profondo possibile.
Puoi specificare un componente del percorso come carattere jolly racchiudendolo tra parentesi graffe; ref('foo/{bar}') corrisponde a qualsiasi elemento secondario di /foo. I valori di questi
componenti del percorso con caratteri jolly sono disponibili nell'
EventContext.params
oggetto della tua funzione. In questo esempio, il valore è disponibile come context.params.bar.
I percorsi con caratteri jolly possono corrispondere a più eventi di una singola scrittura. Un inserto di
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
corrisponde due volte al percorso "/foo/{bar}": una volta con "hello": "world"
e una volta con "firebase": "functions".
Gestire i dati sugli eventi
Quando gestisci un evento Realtime Database, l'oggetto dati restituito è un
DataSnapshot.
Per gli eventi onWrite o onUpdate, il primo parametro è un oggetto Change che contiene due snapshot che rappresentano lo stato dei dati prima e dopo l'evento di attivazione. Per gli eventi onCreate e onDelete, l'oggetto dati restituito è uno snapshot dei dati creati o eliminati.
In questo esempio, la funzione recupera lo snapshot per il percorso specificato, converte la stringa in quella posizione in maiuscolo e scrive la stringa modificata nel database:
// 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); });
Accedere alle informazioni sull'autenticazione utente
Da EventContext.auth
e EventContext.authType,
puoi accedere
alle informazioni utente, incluse le autorizzazioni, per l'utente che ha attivato
una funzione. Questo può essere utile per applicare le regole di sicurezza, consentendo alla funzione di completare operazioni diverse in base al livello di autorizzazioni dell'utente:
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);
}
});
Inoltre, puoi sfruttare le informazioni sull'autenticazione utente per "impersonare" un utente ed eseguire operazioni di scrittura per suo conto. Assicurati di eliminare l'istanza dell'app come mostrato di seguito per evitare problemi di concorrenza:
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));
});
});
Leggere il valore precedente
L'Change oggetto ha una
before
proprietà che ti consente di ispezionare ciò che è stato salvato in Realtime Database prima dell'
evento. La proprietà before restituisce un DataSnapshot in cui tutti i
metodi (ad esempio,
val() e
exists())
fanno riferimento al valore precedente. Puoi leggere di nuovo il nuovo valore utilizzando
l'oggetto DataSnapshot originale o leggendo la
after
proprietà. Questa proprietà su qualsiasi Change è un altro DataSnapshot che rappresenta lo stato dei dati dopo che si è verificato l'evento.
Ad esempio, la proprietà before può essere utilizzata per assicurarsi che la funzione metta in maiuscolo il testo solo quando viene creato per la prima volta:
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);
});