Firebase Realtime Database トリガー

Cloud Functions を使用すると、クライアント コードを更新することなく、Firebase Realtime Database 内のイベントを処理できます。Cloud Functions では、完全な管理者権限を使用してデータベース オペレーションを実行できます。また、データベースに対する個々の変更がそれぞれ別個に処理されます。Firebase Realtime Database の変更は、Firebase Admin SDK で行うことができます。

一般的なライフサイクルの場合、Firebase Realtime Database の関数は以下のように機能します。

  1. 特定のデータベースに変更が加えられるのを待ちます。

  2. イベントが発生するとトリガーされ、そのタスクを実行します。

  3. 指定されたドキュメントに保存されているデータのスナップショットを含むデータ オブジェクトを受け取ります。

イベントタイプ

関数を使用して、2 つのレベルの特性でデータベース イベントを処理できます。作成イベント、更新イベントまたは削除イベントのみリッスンすることも、パスに対するあらゆる種類の変更をリッスンすることもできます。Cloud Functions は、Realtime Database で次のイベントタイプをサポートしています。

イベントタイプ トリガー
providers/google.firebase.database/eventTypes/ref.write 任意のミューテーション イベントでトリガーされます。Realtime Database 内でデータが作成、更新、削除されるとトリガーされます。
providers/google.firebase.database/eventTypes/ref.create(デフォルト) Realtime Database で新しいデータが作成されるとトリガーされます。
providers/google.firebase.database/eventTypes/ref.update Realtime Database でデータが更新されるとトリガーされます。
providers/google.firebase.database/eventTypes/ref.delete Realtime Database からデータが削除されるとトリガーされます。

データベース パスとインスタンスの指定

関数がトリガーされるタイミングと場所を制御するには、パスを指定する必要があります。必要であれば、データベース インスタンスも指定します。

パス

パスを指定すると、そのパスに関係するすべての書き込みが比較対象となります。指定したパスの下にある任意の場所で発生する書き込みも対象に含まれます。関数のパスを /foo/bar として設定すると、次の場所のイベントはいずれもマッチ対象となります。

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

どちらの場合も、Firebase ではイベントが /foo/bar で発生したと解釈され、イベントデータには /foo/bar の古いデータと新しいデータが含まれます。イベントデータが大きい場合は、データベースのルート付近で単一の関数を使用する代わりに、より深いパスで複数の関数を使用することを検討してください。最高のパフォーマンスを得るには、できるだけ深いレベルのデータのみを要求するようにします。

パス コンポーネントは、中かっこで囲むことでワイルドカードとして指定できます。foo/{bar}/foo のすべての子と一致します。このワイルドカード パス コンポーネントの値は、関数の event.params オブジェクト内で使用できます。この例では、値は event.params.bar として使用可能になります。

ワイルドカードを含むパスは、1 つの書き込みの複数のイベントに一致する場合があります。次の挿入を見てみましょう。

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

これはパス /foo/{bar} と 2 回一致します。1 回目は "hello": "world" で、2 回目は "firebase": "functions" です。

インスタンス

Cloud Console を使用する場合、インスタンスを指定しないと、プロジェクトのデフォルトのデータベース インスタンスに関数がデプロイされます。

gcloud コマンドライン ツールを使用する場合は、--trigger-resource 文字列の一部としてインスタンスを指定する必要があります。通常、デフォルトのインスタンス名はプロジェクト ID と同じです。

たとえば、my-project-id という ID のプロジェクトに関数をデプロイする場合、--trigger-resource 文字列を次のように使用します。

--trigger-resource projects/_/instances/my-project-id/refs/PATH

イベントの構造

Realtime Database イベントが処理されると、data オブジェクトには 2 つのプロパティが JSON オブジェクト形式で含まれます。

  • data: 関数をトリガーしたイベントの前に取得されたデータのスナップショット。

  • delta: 関数をトリガーしたイベントの後に取得されたデータのスナップショット。

コードサンプル

Node.js

/**
 * Triggered by a change to a Firebase RTDB reference.
 *
 * @param {!Object} event The Cloud Functions event.
 */
exports.helloRTDB = event => {
  const triggerResource = event.resource;

  const pathParams = event.params;
  if (pathParams) {
    console.log('Path parameters:');
    Object.keys(pathParams).forEach(key => {
      console.log(`  ${key}: ${pathParams[key]}`);
    });
  }

  console.log(`Function triggered by change to: ${triggerResource}`);
  console.log(`Admin?: ${!!event.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

    if 'params' in data:
        print('Path parameters:')
        for param, value in data['params'].items():
            print(f'  {param}: {value}')

    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: %v", 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;
        }
    }
}

関数のデプロイ

次の gcloud コマンドは、パス /messages/{pushId}/originalcreate イベントによってトリガーされる関数をデプロイします。

gcloud functions deploy YOUR_FUNCTION_NAME \
  --trigger-event providers/google.firebase.database/eventTypes/ref.create \
  --trigger-resource projects/_/instances/DATABASE_INSTANCE/refs/messages/{pushId}/original \
  --runtime RUNTIME
引数 説明
--trigger-event NAME 関数が受信するイベントタイプの名前。この場合、write、create、update、delete のいずれかになります。
--trigger-resource NAME 関数がリッスンするデータベース パスの完全修飾名。次の形式に従う必要があります。 projects/_/instances/DATABASE_INSTANCE/refs/PATH
--runtime RUNTIME 使用しているランタイムの名前。網羅的なリストについては、gcloud リファレンスをご覧ください。