有了 Cloud Functions,您就能在 Firebase Realtime Database 中處理事件,而無需更新用戶端程式碼。Cloud Functions 可讓您以完整的管理權限執行 Realtime Database 作業,並確保對 Realtime Database 的每項變更都會個別處理。您可以透過資料快照或 Admin SDK 變更 Firebase Realtime Database。
在典型的生命週期中,Firebase Realtime Database 函式會執行以下操作:
- 等待特定 Realtime Database 路徑的變更。
- 在事件發生時觸發並執行它的工作。
- 接收包含該路徑中儲存資料快照的資料物件。
您可以觸發函式,回應 Firebase Realtime Database 中資料庫節點的寫入、建立、更新或刪除作業。如要控制函式觸發時機,請指定其中一個事件處理常式,並指定要監聽事件的 Realtime Database 路徑。
設定函式位置
Realtime Database 例項位置與函式位置之間的距離可能會造成嚴重的網路延遲。此外,區域之間的不一致性也可能導致部署失敗。為避免發生這種情況,請指定函式位置,讓其與資料庫執行個體位置相符。
處理 Realtime Database 事件
透過函式,您可以以兩種精確度處理 Realtime Database 事件:您可以專門監聽寫入、建立、更新或刪除事件,也可以監聽參照項目的任何變更。
以下是用於回應 Realtime Database 事件的處理常式:
Node.js
onValueWritten()
在 Realtime Database 中建立、更新或刪除資料時觸發。onValueCreated()
只有在 Realtime Database 中建立資料時才會觸發。onValueUpdated()
只有在 Realtime Database 中更新資料時才會觸發。onValueDeleted()
只有在 Realtime Database 中刪除資料時才會觸發。
Python
on_value_written()
在 Realtime Database 中建立、更新或刪除資料時觸發。on_value_created()
只有在 Realtime Database 中建立資料時才會觸發。on_value_updated()
只有在 Realtime Database 中更新資料時才會觸發。on_value_deleted()
只有在 Realtime Database 中刪除資料時才會觸發。
匯入必要模組
您必須在函式來源中匯入要使用的 SDK 模組。在這個範例中,您必須匯入 HTTP 和 Realtime Database 模組,以及用於寫入 Realtime Database 的 Firebase Admin SDK 模組。
Node.js
// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onRequest} = require("firebase-functions/v2/https");
const {onValueCreated} = require("firebase-functions/v2/database");
const {logger} = require("firebase-functions");
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp();
Python
# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import db_fn, https_fn
# The Firebase Admin SDK to access the Firebase Realtime Database.
from firebase_admin import initialize_app, db
app = initialize_app()
指定執行個體和路徑
如要控管函式應在何時何處觸發,請使用路徑和選用的 Realtime Database 例項設定函式。如果您未指定例項,函式會監聽函式區域中的所有 Realtime Database 例項。您也可以指定 Realtime Database 執行個體模式,將部署作業指定給同一地區的部分執行個體。
例如:
Node.js
// All Realtime Database instances in default function region us-central1 at path "/user/{uid}" // There must be at least one Realtime Database present in us-central1. const onWrittenFunctionDefault = onValueWritten("/user/{uid}", (event) => { // … }); // Instance named "my-app-db-2", at path "/user/{uid}". // The "my-app-db-2" instance must exist in this region. const OnWrittenFunctionInstance = onValueWritten( { ref: "/user/{uid}", instance: "my-app-db-2" // This example assumes us-central1, but to set location: // region: "europe-west1" }, (event) => { // … } ); // Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com. // There must be at least one Realtime Database with "my-app-db-*" prefix in this region. const onWrittenFunctionInstance = onValueWritten( { ref: "/user/{uid=*@gmail.com}", instance: "my-app-db-*" // This example assumes us-central1, but to set location: // region: "europe-west1" }, (event) => { // … } );
Python
# All Realtime Database instances in default function region us-central1 at path "/user/{uid}"
# There must be at least one Realtime Database present in us-central1.
@db_fn.on_value_written(r"/user/{uid}")
def onwrittenfunctiondefault(event: db_fn.Event[db_fn.Change]):
# ...
pass
# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
@db_fn.on_value_written(
reference=r"/user/{uid}",
instance="my-app-db-2",
# This example assumes us-central1, but to set location:
# region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
# ...
pass
# Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
# There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
@db_fn.on_value_written(
reference=r"/user/{uid=*@gmail.com}",
instance="my-app-db-*",
# This example assumes us-central1, but to set location:
# region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
# ...
pass
這些參數會指示函式在 Realtime Database 例項的特定路徑中處理寫入作業。
路徑規格會比對觸及路徑的「所有」寫入作業,包括在路徑下方任何位置發生的寫入作業。如果您將函式的路徑設為 /foo/bar
,則會比對下列兩個位置的事件:
/foo/bar
/foo/bar/baz/really/deep/path
無論是哪種情況,Firebase 都會解讀事件發生在 /foo/bar
,而事件資料會包含 /foo/bar
的舊資料和新資料。如果事件資料可能很大,請考慮在較深的路徑中使用多個函式,而非在資料庫根目錄附近使用單一函式。為獲得最佳效能,請只在最深層級別要求資料。
萬用字元和擷取
您可以使用 {key}
、{key=*}
、{key=prefix*}
、{key=*suffix}
進行擷取。*
、prefix*
、*suffix
:用於單一區段的萬用字元。注意:**
代表多段萬用字元,Realtime Database 不支援這類萬用字元。請參閱「瞭解路徑模式」。
路徑萬用字元。您可以將路徑元件指定為萬用字元:
- 使用星號
*
。舉例來說,foo/*
會比對foo/
以下節點階層的任何子項一層。 - 使用包含星號的區隔
*
。舉例來說,foo/app*-us
會比對foo/
下方任何子區段,且前置字串為app
,後置字串為-us
。
含有萬用字元的路徑可比對多個事件,例如單一寫入事件。插入
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
比對路徑 "/foo/*"
兩次:一次使用 "hello": "world"
,另一次使用 "firebase": "functions"
。
路徑擷取。您可以將路徑比對結果擷取到命名變數中,以便在函式程式碼中使用 (例如 /user/{uid}
、/user/{uid=*-us}
)。
擷取變數的值會顯示在函式的 database.DatabaseEvent.params 物件中。
執行個體萬用字元:您也可以使用萬用字元指定例項元件。例項萬用字元可以包含前置字元、後置字元或兩者皆有 (例如 my-app-*-prod
)。
萬用字元和擷取參照
在 Cloud Functions (第 2 代) 和 Realtime Database 中,您可以在指定 ref
和 instance
時使用模式。每個觸發事件介面都會提供下列函式範圍選項:
指定 ref |
指定 instance |
行為 |
---|---|---|
單曲 (/foo/bar ) |
未指定 | 將處理程序範圍限定為函式區域中的所有例項。 |
單曲 (/foo/bar ) |
單曲 (‘my-new-db' ) |
將處理常式限制在函式區域中的特定例項。 |
單曲 (/foo/bar ) |
圖案 (‘inst-prefix*' ) |
將處理程序範圍限定為函式區域中符合模式的所有例項。 |
圖案 (/foo/{bar} ) |
未指定 | 將處理程序範圍限定為函式區域中的所有例項。 |
圖案 (/foo/{bar} ) |
單曲 (‘my-new-db' ) |
將處理常式限制在函式區域中的特定例項。 |
圖案 (/foo/{bar} ) |
圖案 (‘inst-prefix*' ) |
將處理程序範圍限定為函式區域中符合模式的所有例項。 |
處理事件資料
Realtime Database 事件觸發時,會將 Event
物件傳遞至處理常式函式。這個物件具有 data
屬性,針對建立和刪除事件,其中包含建立或刪除的資料快照。
在這個範例中,函式會擷取參照路徑的資料,將該位置的字串轉換為大寫,並將修改過的字串寫入資料庫:
Node.js
// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
// for all databases in 'us-central1'
exports.makeuppercase = onValueCreated(
"/messages/{pushId}/original",
(event) => {
// Grab the current value of what was written to the Realtime Database.
const original = event.data.val();
logger.log("Uppercasing", event.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing
// asynchronous tasks inside a function, such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the
// Realtime Database returns a Promise.
return event.data.ref.parent.child("uppercase").set(uppercase);
},
);
Python
@db_fn.on_value_created(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[Any]) -> None:
"""Listens for new messages added to /messages/{pushId}/original and
creates an uppercase version of the message to /messages/{pushId}/uppercase
"""
# Grab the value that was written to the Realtime Database.
original = event.data
if not isinstance(original, str):
print(f"Not a string: {event.reference}")
return
# Use the Admin SDK to set an "uppercase" sibling.
print(f"Uppercasing {event.params['pushId']}: {original}")
upper = original.upper()
parent = db.reference(event.reference).parent
if parent is None:
print("Message can't be root node.")
return
parent.child("uppercase").set(upper)
讀取先前的值
對於 write
或 update
事件,data
屬性是 Change
物件,其中包含兩個快照,分別代表觸發事件前後的資料狀態。Change
物件具有 before
屬性,可讓您檢查事件發生前儲存至 Realtime Database 的內容,以及代表事件發生後資料狀態的 after
屬性。
舉例來說,您可以使用 before
屬性,確保函式在首次建立時只會將文字轉為大寫:
Node.js
exports makeUppercase = onValueWritten("/messages/{pushId}/original", (event) => { // Only edit data when it is first created. if (event.data.before.exists()) { return null; } // Exit when the data is deleted. if (!event.data.after.exists()) { return null; } // Grab the current value of what was written to the Realtime Database. const original = event.data.after.val(); console.log('Uppercasing', event.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 event.data.after.ref.parent.child('uppercase').set(uppercase); });
Python
@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase2(event: db_fn.Event[db_fn.Change]) -> None:
"""Listens for new messages added to /messages/{pushId}/original and
creates an uppercase version of the message to /messages/{pushId}/uppercase
"""
# Only edit data when it is first created.
if event.data.before is not None:
return
# Exit when the data is deleted.
if event.data.after is None:
return
# Grab the value that was written to the Realtime Database.
original = event.data.after
if not hasattr(original, "upper"):
print(f"Not a string: {event.reference}")
return
# Use the Admin SDK to set an "uppercase" sibling.
print(f"Uppercasing {event.params['pushId']}: {original}")
upper = original.upper()
parent = db.reference(event.reference).parent
if parent is None:
print("Message can't be root node.")
return
parent.child("uppercase").set(upper)