使用 Cloud Functions 擴充 Firestore
使用 Cloud Functions,您可以部署 Node.js 程式碼,處理因 Firestore 資料庫變更而觸發的事件。這樣一來,您就能輕鬆將伺服器端功能加入應用程式,不必自行執行伺服器。
。如需使用案例範例,請參閱「我可以使用 Cloud Functions 執行哪些作業?」或 Functions Samples GitHub 存放區。
Firestore 函式觸發條件
Cloud Functions for Firebase SDK 會匯出 functions.firestore
物件,方便您建立與特定 Firestore 事件相關聯的處理常式。
事件類型 | 觸發條件 |
---|---|
onCreate |
在第一次寫入文件時觸發。 |
onUpdate |
在文件已經存在且已變更任何值時觸發。 |
onDelete |
在刪除具有資料的文件時觸發。 |
onWrite |
在觸發 onCreate 、onUpdate 或 onDelete 時觸發。 |
如果您尚未啟用 Cloud Functions for Firebase 專案,請參閱「開始使用:編寫及部署第一個函式」,瞭解如何設定 Cloud Functions for Firebase 專案。
編寫 Firestore 觸發的函式
定義函式觸發條件
如要定義 Firestore 觸發條件,請指定文件路徑和事件類型:
Node.js
const functions = require('firebase-functions');
exports.myFunction = functions.firestore
.document('my-collection/{docId}')
.onWrite((change, context) => { /* ... */ });
指定單一文件
如要針對特定文件的任何變更觸發事件,可以使用下列函式。
Node.js
// Listen for any change on document `marie` in collection `users` exports.myFunctionName = functions.firestore .document('users/marie').onWrite((change, context) => { // ... Your code here });
使用萬用字元指定一組文件
如要將觸發條件附加至一組文件 (例如特定集合中的任何文件),請使用 {wildcard}
取代文件 ID:
Node.js
// Listen for changes in all documents in the 'users' collection exports.useWildcard = functions.firestore .document('users/{userId}') .onWrite((change, context) => { // If we set `/users/marie` to {name: "Marie"} then // context.params.userId == "marie" // ... and ... // change.after.data() == {name: "Marie"} });
在這個範例中,當 users
中任何文件的任何欄位發生變更時,系統會比對名為 userId
的萬用字元。
如果 users
中的文件有子集合,且其中一個子集合的文件欄位發生變更,系統不會觸發 userId
萬用字元。
系統會從文件路徑擷取萬用字元相符項目,並儲存至 context.params
。
您可以定義任意數量的萬用字元,用來取代明確的集合或文件 ID,例如:
Node.js
// Listen for changes in all documents in the 'users' collection and all subcollections exports.useMultipleWildcards = functions.firestore .document('users/{userId}/{messageCollectionId}/{messageId}') .onWrite((change, context) => { // If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then // context.params.userId == "marie"; // context.params.messageCollectionId == "incoming_messages"; // context.params.messageId == "134"; // ... and ... // change.after.data() == {body: "Hello"} });
事件觸發條件
在新文件建立時觸發函式
您可以使用含有萬用字元的 onCreate()
處理常式,在集合中建立新文件時觸發函式。每當新增使用者設定檔時,這個範例函式就會呼叫 createUser
:
Node.js
exports.createUser = functions.firestore .document('users/{userId}') .onCreate((snap, context) => { // Get an object representing the document // e.g. {'name': 'Marie', 'age': 66} const newValue = snap.data(); // access a particular field as you would any JS property const name = newValue.name; // perform desired operations ... });
在文件更新時觸發函式
您也可以使用 onUpdate()
函式和萬用字元,在文件更新時觸發函式。如果使用者變更個人資料,這個範例函式會呼叫 updateUser
:
Node.js
exports.updateUser = functions.firestore .document('users/{userId}') .onUpdate((change, context) => { // Get an object representing the document // e.g. {'name': 'Marie', 'age': 66} const newValue = change.after.data(); // ...or the previous value before this update const previousValue = change.before.data(); // access a particular field as you would any JS property const name = newValue.name; // perform desired operations ... });
在刪除文件時觸發函式
您也可以使用 onDelete()
函式和萬用字元,在刪除文件時觸發函式。在這個函式範例中,使用者刪除使用者設定檔時會呼叫 deleteUser
:
Node.js
exports.deleteUser = functions.firestore .document('users/{userID}') .onDelete((snap, context) => { // Get an object representing the document prior to deletion // e.g. {'name': 'Marie', 'age': 66} const deletedValue = snap.data(); // perform desired operations ... });
針對文件的所有變更觸發函式
如果您不關心觸發的事件類型,可以使用 onWrite()
函式搭配萬用字元,監聽 Firestore 文件中的所有變更。如果使用者建立、更新或刪除,這個範例函式會呼叫 modifyUser
:
Node.js
exports.modifyUser = functions.firestore .document('users/{userID}') .onWrite((change, context) => { // Get an object with the current document value. // If the document does not exist, it has been deleted. const document = change.after.exists ? change.after.data() : null; // Get an object with the previous document value (for update or delete) const oldDocument = change.before.data(); // perform desired operations ... });
讀取及寫入資料
函式觸發時,會提供與事件相關的資料快照。您可以使用這個快照讀取或寫入觸發事件的文件,也可以使用 Firebase Admin SDK 存取資料庫的其他部分。
事件資料
讀取資料
函式觸發時,您可能想從更新的文件取得資料,或取得更新前的資料。您可以使用 change.before.data()
取得先前的資料,其中包含更新前的文件快照。同樣地,change.after.data()
包含更新後的檔案快照狀態。
Node.js
exports.updateUser2 = functions.firestore .document('users/{userId}') .onUpdate((change, context) => { // Get an object representing the current document const newValue = change.after.data(); // ...or the previous value before this update const previousValue = change.before.data(); });
您可以像存取任何其他物件一樣存取屬性。或者,您可以使用 get
函式存取特定欄位:
Node.js
// Fetch data using standard accessors const age = snap.data().age; const name = snap.data()['name']; // Fetch data using built in accessor const experience = snap.get('experience');
寫入資料
每次函式呼叫都會與 Firestore 資料庫中的特定文件建立關聯。您可以透過傳回函式的快照 ref
屬性,以 DocumentReference
形式存取該文件。
這個 DocumentReference
來自 Firestore Node.js SDK,並包含 update()
、set()
和 remove()
等方法,方便您修改觸發函式的文件。
Node.js
// Listen for updates to any `user` document. exports.countNameChanges = functions.firestore .document('users/{userId}') .onUpdate((change, context) => { // Retrieve the current and previous value const data = change.after.data(); const previousData = change.before.data(); // We'll only update if the name has changed. // This is crucial to prevent infinite loops. if (data.name == previousData.name) { return null; } // Retrieve the current count of name changes let count = data.name_change_count; if (!count) { count = 0; } // Then return a promise of a set operation to update the count return change.after.ref.set({ name_change_count: count + 1 }, {merge: true}); });
觸發事件以外的資料
Cloud Functions 會在受信任的環境中執行,因此會以專案中的服務帳戶獲得授權。您可以使用 Firebase Admin SDK 執行讀取和寫入作業:
Node.js
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.writeToFirestore = functions.firestore
.document('some/doc')
.onWrite((change, context) => {
db.doc('some/otherdoc').set({ ... });
});
限制
請注意,Cloud Run 函式的 Firestore 觸發條件有下列限制:
- 如要使用 Cloud Run 函式 (第 1 代),必須先在 Firestore 原生模式中建立「(default)」資料庫。不支援 Firestore 已命名的資料庫或 Datastore 模式。在這種情況下,請使用 Cloud Run functions (第 2 代) 設定事件。
- 我們不保證排序。快速變更可能會以非預期的順序觸發函式呼叫。
- 系統至少會傳送一次事件,但單一事件可能會導致多次函式叫用。請避免依附於「只執行一次」機制,並編寫等冪函式。
- Firestore (Datastore 模式) 需要 Cloud Run 函式 (第 2 代)。Cloud Run 函式 (第 1 代) 不支援 Datastore 模式。
- 觸發條件會與單一資料庫建立關聯。您無法建立與多個資料庫相符的觸發程序。
- 刪除資料庫時,系統不會自動刪除該資料庫的任何觸發程序。觸發條件會停止傳送事件,但會繼續存在,直到您刪除觸發條件為止。
- 如果相符事件超過要求大小上限,事件可能無法傳送至 Cloud Run 函式 (第 1 代)。
- 如果事件因要求大小而未傳送,系統會記錄在平台記錄中, 並計入專案的記錄檔用量。
- 您可以在記錄檔探索器中找到這些記錄,訊息為「Event cannot deliver to Cloud function due to size exceeding the limit for 1st gen...」(事件無法傳送至 Cloud 函式,因為大小超出第 1 代的限制...),嚴重程度為
error
。您可以在「functionName
」欄位下方找到函式名稱。如果receiveTimestamp
欄位仍在一小時內,您可以讀取時間戳記前後的快照,推斷實際活動內容。 - 如要避免這種情況,可以採取下列做法:
- 遷移及升級至 Cloud Run functions (第 2 代)
- 縮小文件
- 刪除有問題的 Cloud Run 函式
- 您可以使用排除條件關閉記錄功能,但請注意,違規事件仍不會傳送。