Firebase Summit で発表されたすべての情報をご覧ください。Firebase を使用してアプリ開発を加速し、自信を持ってアプリを実行する方法を紹介しています。詳細

リアルタイムデータベーストリガー

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

Cloud Functions を使用すると、クライアント コードを更新する必要なく、Firebase Realtime Database でイベントを処理できます。 Cloud Functions を使用すると、Realtime Database 操作を完全な管理者権限で実行でき、Realtime Database への各変更が個別に処理されるようになります。 DataSnapshotまたはAdmin SDKを介して、Firebase Realtime Database の変更を行うことができます。

一般的なライフサイクルでは、Firebase Realtime Database 関数は次のことを行います。

  1. 特定の Realtime Database の場所への変更を待機します。
  2. イベントが発生したときにトリガーし、そのタスクを実行します (ユースケースの例については、 Cloud Functions で何ができますか? を参照してください)。
  3. 指定されたドキュメントに格納されているデータのスナップショットを含むデータ オブジェクトを受け取ります。

Realtime Database 関数をトリガーする

functions.databaseを使用して、Realtime Database イベント用の新しい関数を作成します。関数がいつトリガーされるかを制御するには、イベント ハンドラーの 1 つを指定し、イベントをリッスンする Realtime Database パスを指定します。

イベント ハンドラーを設定する

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

  • onWrite() : Realtime Database でデータが作成、更新、または削除されたときにトリガーされます。
  • onCreate() : Realtime Database で新しいデータが作成されたときにトリガーされます。
  • onUpdate() : Realtime Database でデータが更新されたときにトリガーされます。
  • onDelete() : データが Realtime Database から削除されたときにトリガーされます。

インスタンスとパスを指定

関数をトリガーするタイミングと場所を制御するには、 ref(path)を呼び出してパスを指定し、必要に応じてinstance('INSTANCE_NAME')で Realtime Database インスタンスを指定します。インスタンスを指定しない場合、関数は Firebase プロジェクトのデフォルトの Realtime Database インスタンスにデプロイされます。次に例を示します。

  • デフォルトの Realtime Database インスタンス: functions.database.ref('/foo/bar')
  • 「my-app-db-2」という名前のインスタンス: functions.database.instance('my-app-db-2').ref('/foo/bar')

これらのメソッドは、Realtime Database インスタンス内の特定のパスで書き込みを処理するように関数に指示します。パスの仕様は、パスの下のどこかで発生する書き込みを含め、パスに触れるすべての書き込みに一致します。関数のパスを/foo/barとして設定すると、次の両方の場所のイベントに一致します。

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

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

中かっこで囲むことにより、パス コンポーネントをワイルドカードとして指定できます。 ref('foo/{bar}') /fooの任意の子に一致します。これらのワイルドカード パス コンポーネントの値は、関数のEventContext.paramsオブジェクト内で使用できます。この例では、値はcontext.params.barとして使用できます。

ワイルドカードを含むパスは、1 回の書き込みで複数のイベントに一致できます。の挿入

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

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

イベント データの処理

Realtime Database イベントを処理する場合、返されるデータ オブジェクトはDataSnapshotです。 onWriteまたはonUpdateイベントの場合、最初のパラメーターは、トリガー イベントの前後のデータ状態を表す 2 つのスナップショットを含むChangeオブジェクトです。 onCreateおよびonDeleteイベントの場合、返されるデータ オブジェクトは、作成または削除されたデータのスナップショットです。

この例では、関数は指定されたパスのスナップショットを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.authおよびEventContext.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プロパティがあり、イベントの前にRealtime Database に保存された内容を調べることができます。 beforeプロパティは、すべてのメソッド ( val()exists()など) が前の値を参照するDataSnapshotを返します。元の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);
    });