借助 Cloud Functions,您可以處理 Firebase 即時資料庫中的事件,而無需更新客戶端程式碼。 Cloud Functions 可讓您以完全管理權限執行即時資料庫操作,並確保對即時資料庫的每個變更都單獨處理。您可以透過DataSnapshot
或Admin SDK變更 Firebase 即時資料庫。
在典型的生命週期中,Firebase 即時資料庫函數執行以下操作:
- 等待特定即時資料庫位置的變更。
- 當事件發生並執行其任務時觸發(有關用例範例,請參閱我可以使用 Cloud Functions 做什麼? )。
- 接收一個資料對象,其中包含指定文件中儲存的資料的快照。
觸發即時資料庫功能
使用functions.database
為即時資料庫事件建立新函數。若要控制函數何時觸發,請指定事件處理程序之一,並指定它將偵聽事件的即時資料庫路徑。
設定事件處理程序
函數可讓您在兩個特定層級上處理即時資料庫事件;您可以專門偵聽建立、更新或刪除事件,也可以偵聽路徑的任何類型的變更。 Cloud Functions 支援即時資料庫的以下事件處理程序:
-
onWrite()
,當即時資料庫中建立、更新或刪除資料時觸發。 -
onCreate()
,在即時資料庫中建立新資料時觸發。 -
onUpdate()
,當即時資料庫中的資料更新時觸發。 -
onDelete()
,當從即時資料庫中刪除資料時觸發。
指定實例和路徑
若要控制函數應在何時何地觸發,請呼叫ref(path)
來指定路徑,並可以選擇使用instance('INSTANCE_NAME')
指定即時資料庫實例。如果您不指定實例,則函數將部署到 Firebase 專案的預設即時資料庫實例,例如:
- 預設即時資料庫實例:
functions.database.ref('/foo/bar')
- 名為「my-app-db-2」的實例:
functions.database.instance('my-app-db-2').ref('/foo/bar')
這些方法指示您的函數處理即時資料庫執行個體中特定路徑的寫入。路徑規範與接觸路徑的所有寫入相匹配,包括發生在其下方任何位置的寫入。如果將函數的路徑設為/foo/bar
,它將符合這兩個位置的事件:
/foo/bar
/foo/bar/baz/really/deep/path
無論哪種情況,Firebase 都會解釋為事件發生在/foo/bar
,事件資料包括/foo/bar
處的舊資料和新資料。如果事件資料可能很大,請考慮在更深的路徑中使用多個函數,而不是在資料庫根附近使用單一函數。為了獲得最佳效能,僅請求盡可能深的級別的資料。
您可以透過用大括號將路徑組件指定為通配符; ref('foo/{bar}')
符合/foo
的任何子級。這些通配符路徑組件的值在函數的EventContext.params
物件中可用。在此範例中,該值可用作context.params.bar
。
帶有通配符的路徑可以匹配一次寫入的多個事件。插入一個
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
匹配路徑"/foo/{bar}"
兩次:一次與"hello": "world"
,再次與"firebase": "functions"
。
處理事件數據
處理即時資料庫事件時,傳回的資料物件是DataSnapshot
。對於onWrite
或onUpdate
事件,第一個參數是Change
對象,其中包含兩個快照,分別表示觸發事件之前和之後的資料狀態。對於onCreate
和onDelete
事件,傳回的資料物件是建立或刪除的資料的快照。
在此範例中,函數會擷取指定路徑的快照,將該位置的字串轉換為大寫,並將修改後的字串寫入資料庫:
// 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); });
存取用戶認證資訊
從EventContext.auth
和EventContext.authType
,您可以存取觸發函數的使用者的使用者訊息,包括權限。這對於執行安全規則非常有用,允許您的函數根據使用者的權限等級完成不同的操作:
const functions = require('firebase-functions');
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);
}
});
此外,您還可以利用使用者驗證資訊來「模擬」使用者並代表使用者執行寫入操作。確保刪除應用程式實例,如下所示,以防止並發問題:
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));
});
});
讀取之前的值
Change
物件有一個before
屬性,可讓您檢查事件發生前已儲存到即時資料庫的內容。 before
屬性傳回一個DataSnapshot
,其中所有方法(例如val()
和exists()
)都引用前一個值。您可以透過使用原始DataSnapshot
或讀取after
屬性來再次讀取新值。任何Change
上的此屬性都是另一個DataSnapshot
表示事件發生後資料的狀態。
例如, before
屬性可用於確保函數在首次建立時僅使用大寫文字:
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);
});