Firebase 即時資料庫觸發條件

您可以使用 Cloud Run 函式,在與函式相同的 Google Cloud 專案中處理 Firebase 即時資料庫中的事件。Cloud Run 函式可讓您以完整的管理權限執行資料庫作業,並確保資料庫的每項變更都會個別處理。您可以透過 Firebase Admin SDK 變更 Firebase 即時資料庫。

在一般生命週期中,Firebase 即時資料庫函式會執行下列操作:

  1. 等待對特定資料庫位置的變更。

  2. 在事件發生時觸發並執行它的工作。

  3. 接收包含儲存在指定文件中之資料快照的資料物件。

事件類型

函式可讓您以兩個明確性層級處理資料庫事件;您可以明確接聽僅建立、更新或刪除事件,也可以接聽任何種類路徑的任何變更。Cloud Run 函式支援 Realtime Database 的以下事件類型:

事件類型 觸發條件
providers/google.firebase.database/eventTypes/ref.write 針對任何異動事件觸發:當在即時資料庫中建立、更新或刪除資料時。
providers/google.firebase.database/eventTypes/ref.create (預設) 當在即時資料庫中建立新資料時觸發。
providers/google.firebase.database/eventTypes/ref.update 當在即時資料庫中更新資料時觸發。
providers/google.firebase.database/eventTypes/ref.delete 當在即時資料庫中刪除資料時觸發。

指定資料庫路徑與執行個體

如要控制您函式應觸發的時機與位置,需要指定路徑,然後選擇性地指定資料庫執行個體。

路徑

路徑規格會與涉及路徑的「所有」寫入內容進行比對,其中包括在其下方任何位置發生的寫入。如果您將函式的路徑設為 /foo/bar,則會比對下列兩個位置的事件:

 /foo/bar
 /foo/bar/baz/really/deep/path

無論是哪種情況,Firebase 都會解讀事件發生在 /foo/bar,而事件資料會包含 /foo/bar 的舊資料和新資料。如果事件資料比較大,請考慮使用較深路徑的多個函式,而不是靠近資料庫根目錄的單一函式。如要獲得最佳效能,請僅要求可能的最深層級資料。

您可以將路徑元件指定為萬用字元,方法是將其前後加上大括號;foo/{bar} 會比對 /foo 的任何子項。這些萬用字元路徑元件的值會顯示在函式的 event.params 物件中。在這個範例中,值為 event.params.bar

使用萬用字元的路徑可以與單一寫入內容中的多個事件進行比對。插入:

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

比對路徑 /foo/{bar} 兩次:一次使用 "hello": "world",另一次使用 "firebase": "functions"

執行個體

使用 Google Cloud 控制台時,必須指定資料庫例項。

使用 Google Cloud CLI 時,必須將執行個體指定為 --trigger-resource 字串的一部分。

例如,以下會在 --trigger-resource 字串中使用下列內容:

--trigger-resource projects/_/instances/DATABASE_INSTANCE/refs/PATH

事件結構

處理即時資料庫事件時,data 物件會包含兩個以 JSON 物件格式提供的屬性:

  • data:在觸發函式的事件發生前,擷取的資料快照。

  • delta:觸發函式的事件發生後所擷取的資料快照。

程式碼範例

Node.js

/**
 * Background Function triggered by a change to a Firebase RTDB reference.
 *
 * @param {!Object} event The Cloud Functions event.
 * @param {!Object} context The Cloud Functions event context.
 */
exports.helloRTDB = (event, context) => {
  const triggerResource = context.resource;

  console.log(`Function triggered by change to: ${triggerResource}`);
  console.log(`Admin?: ${!!context.auth.admin}`);
  console.log('Delta:');
  console.log(JSON.stringify(event.delta, null, 2));
};

Python

import json

def hello_rtdb(data, context):
    """Triggered by a change to a Firebase RTDB reference.
    Args:
        data (dict): The event payload.
        context (google.cloud.functions.Context): Metadata for the event.
    """
    trigger_resource = context.resource

    print("Function triggered by change to: %s" % trigger_resource)
    print("Admin?: %s" % data.get("admin", False))
    print("Delta:")
    print(json.dumps(data["delta"]))

Go


// Package p contains a Cloud Function triggered by a Firebase Realtime Database
// event.
package p

import (
	"context"
	"fmt"
	"log"

	"cloud.google.com/go/functions/metadata"
)

// RTDBEvent is the payload of a RTDB event.
type RTDBEvent struct {
	Data  interface{} `json:"data"`
	Delta interface{} `json:"delta"`
}

// HelloRTDB handles changes to a Firebase RTDB.
func HelloRTDB(ctx context.Context, e RTDBEvent) error {
	meta, err := metadata.FromContext(ctx)
	if err != nil {
		return fmt.Errorf("metadata.FromContext: %w", err)
	}
	log.Printf("Function triggered by change to: %v", meta.Resource)
	log.Printf("%+v", e)
	return nil
}

Java

import com.google.cloud.functions.Context;
import com.google.cloud.functions.RawBackgroundFunction;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.util.logging.Logger;

public class FirebaseRtdb implements RawBackgroundFunction {
  private static final Logger logger = Logger.getLogger(FirebaseRtdb.class.getName());

  // Use GSON (https://github.com/google/gson) to parse JSON content.
  private static final Gson gson = new Gson();

  @Override
  public void accept(String json, Context context) {
    logger.info("Function triggered by change to: " + context.resource());

    JsonObject body = gson.fromJson(json, JsonObject.class);

    boolean isAdmin = false;
    if (body != null && body.has("auth")) {
      JsonObject authObj = body.getAsJsonObject("auth");
      isAdmin = authObj.has("admin") && authObj.get("admin").getAsBoolean();
    }

    logger.info("Admin?: " + isAdmin);

    if (body != null && body.has("delta")) {
      logger.info("Delta:");
      logger.info(body.get("delta").toString());
    }
  }
}

C#

using CloudNative.CloudEvents;
using Google.Cloud.Functions.Framework;
using Google.Events.Protobuf.Firebase.Database.V1;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

namespace FirebaseRtdb;

public class Function : ICloudEventFunction<ReferenceEventData>
{
    private readonly ILogger _logger;

    public Function(ILogger<Function> logger) =>
        _logger = logger;

    public Task HandleAsync(CloudEvent cloudEvent, ReferenceEventData data, CancellationToken cancellationToken)
    {
        _logger.LogInformation("Function triggered by change to {subject}", cloudEvent.Subject);
        _logger.LogInformation("Delta: {delta}", data.Delta);

        // In this example, we don't need to perform any asynchronous operations, so the
        // method doesn't need to be declared async.
        return Task.CompletedTask;
    }
}

Ruby

require "functions_framework"

# Triggered by a change to a Firebase RTDB document.
FunctionsFramework.cloud_event "hello_rtdb" do |event|
  # Event-triggered Ruby functions receive a CloudEvents::Event::V1 object.
  # See https://cloudevents.github.io/sdk-ruby/latest/CloudEvents/Event/V1.html
  # The Firebase event payload can be obtained from the `data` field.
  payload = event.data

  logger.info "Function triggered by change to: #{event.source}"
  logger.info "Admin?: #{payload.fetch 'admin', false}"
  logger.info "Delta: #{payload['delta']}"
end

PHP

use Google\CloudFunctions\CloudEvent;

function firebaseRTDB(CloudEvent $cloudevent)
{
    $log = fopen(getenv('LOGGER_OUTPUT') ?: 'php://stderr', 'wb');

    fwrite($log, 'Event: ' . $cloudevent->getId() . PHP_EOL);

    $data = $cloudevent->getData();
    $resource = $data['resource'] ?? '<null>';

    fwrite($log, 'Function triggered by change to: ' . $resource . PHP_EOL);

    $isAdmin = isset($data['auth']['admin']) && $data['auth']['admin'] == true;

    fwrite($log, 'Admin?: ' . var_export($isAdmin, true) . PHP_EOL);
    fwrite($log, 'Delta: ' . json_encode($data['delta'] ?? '') . PHP_EOL);
}

部署函式

下列 gcloud 指令會部署函式,該函式會在路徑 /messages/{pushId}/original 上由 create 事件觸發:

gcloud functions deploy FUNCTION_NAME \
  --no-gen2 \
  --entry-point ENTRY_POINT \
  --trigger-event providers/google.firebase.database/eventTypes/ref.create \
  --trigger-resource projects/_/instances/DATABASE_INSTANCE/refs/messages/{pushId}/original \
  --runtime RUNTIME
引數 說明
FUNCTION_NAME 您要部署的 Cloud Run 函式註冊名稱。這可以是原始碼中的函式名稱,也可以是任意字串。如果 FUNCTION_NAME 是任意字串,則必須加入 --entry-point 旗標。
--entry-point ENTRY_POINT 原始碼中函式或類別的名稱。選用,除非您未使用 FUNCTION_NAME 指定要於部署期間執行的原始碼中的函式。在這種情況下,您必須使用 --entry-point 提供可執行函式的名稱。
--trigger-event NAME 函式希望接收的事件類型名稱。在這種情況下,會是下列其中一種:寫入、建立、更新或刪除。
--trigger-resource NAME 函式要監聽的完整資料庫路徑。格式應符合下列格式:projects/_/instances/DATABASE_INSTANCE/refs/PATH
--runtime RUNTIME 您使用的執行階段名稱。如需完整清單,請參閱 gcloud 參考資料