Join us in person and online for Firebase Summit on October 18, 2022. Learn how Firebase can help you accelerate app development, release your app with confidence, and scale with ease. Register now

實時數據庫觸發器

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

借助 Cloud Functions,您無需更新客戶端代碼即可處理 Firebase 實時數據庫中的事件。 Cloud Functions 允許您以完全管理權限運行實時數據庫操作,並確保對實時數據庫的每次更改都單獨處理。您可以通過DataSnapshot或通過Admin SDK對 Firebase 實時數據庫進行更改。

在典型的生命週期中,Firebase 實時數據庫函數會執行以下操作:

  1. 等待對特定實時數據庫位置的更改。
  2. 當事件發生並執行其任務時觸發(請參閱我可以使用 Cloud Functions 做什麼?有關用例的示例)。
  3. 接收包含存儲在指定文檔中的數據的快照的數據對象。

觸發實時數據庫函數

使用 functions.database 為實時數據庫事件創建新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 。對於onWriteonUpdate事件,第一個參數是一個Change對象,其中包含兩個快照,表示觸發事件之前和之後的數據狀態。對於onCreateonDelete事件,返回的數據對像是創建或刪除數據的快照。

在此示例中,該函數將指定路徑的快照檢索為snap ,將該位置的字符串轉換為大寫,並將修改後的字符串寫入數據庫:

// 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.authEventContext.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);
    });