有了 Cloud Functions,您就能在
Firebase Realtime Database中處理事件,不必更新用戶端程式碼。
Cloud Functions 可讓您以完整管理員權限執行 Realtime Database 作業,並確保 Realtime Database 的每項變更都會個別處理。您可以透過Firebase Realtime DatabaseDataSnapshot
或 Admin SDK 進行變更。
在一般生命週期中,Firebase Realtime Database 函式會執行下列操作:
- 等待特定 Realtime Database 位置的變更。
- 在發生事件時觸發並執行工作 (請參閱「Cloud Functions 可以做什麼?」查看使用案例範例)。
- 接收資料物件,其中包含指定文件中儲存的資料快照。
觸發 Realtime Database 函式
為 Realtime Database 事件建立新函式
使用 functions.database
。如要控管函式觸發時間,請指定其中一個事件處理常式,並指定要監聽事件的 Realtime Database 路徑。
設定事件處理常式
函式可讓您處理兩個層級的特定 Realtime Database 事件;您可以只監聽建立、更新或刪除事件,也可以監聽路徑的任何變更。Cloud Functions 支援 Realtime Database 的下列事件處理常式:
onWrite()
,會在 Realtime Database 中建立、更新或刪除資料時觸發。onCreate()
,會在 Realtime Database 中建立新資料時觸發。onUpdate()
,會在 Realtime Database 中的資料更新時觸發。onDelete()
,會在資料從 Realtime Database 中刪除時觸發。
指定執行個體和路徑
如要控管函式的觸發時間和位置,請呼叫 ref(path)
指定路徑,並視需要使用 instance('INSTANCE_NAME')
指定 Realtime Database 例項。如未指定執行個體,函式會部署至 Firebase 專案的預設 Realtime Database 執行個體。舉例來說:
- 預設 Realtime Database 執行個體:
functions.database.ref('/foo/bar')
- 名為「my-app-db-2」的執行個體:
functions.database.instance('my-app-db-2').ref('/foo/bar')
這些方法會指示函式處理 Realtime Database 執行個體內特定路徑的寫入作業。路徑規格會比對所有觸及路徑的寫入作業,包括發生在路徑下方任何位置的寫入作業。如果將函式的路徑設為 /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"
。
處理事件資料
處理 Realtime Database 事件時,傳回的資料物件為 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/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);
}
});
此外,您也可以利用使用者驗證資訊「模擬」使用者,並代表使用者執行寫入作業。請務必刪除應用程式例項,如下所示,以免發生並行問題:
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
屬性,可讓您檢查在事件發生「前」儲存至 Realtime Database 的內容。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);
});