Cloud Firestore(ベータ版)の発表: Firebase と Google Cloud Platform から新しいスケーラブルで、フレキシブルなデータベースを利用できるようになりました。詳しくは、Cloud Firestore のドキュメントをご覧ください。

Cloud Functions による Realtime Database の拡張

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

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

  1. 特定のデータベースに変更が加えられるのを待ちます。
  2. イベントが発生するとトリガーされ、そのタスクを実行します(ユースケースについては、Cloud Functions で可能な処理 をご覧ください)。
  3. 指定されたドキュメントに保存されているデータのスナップショットを含むデータ オブジェクトを受け取ります。

データベース関数をトリガーする

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

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

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

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

データベース インスタンスとパスを指定する

関数がトリガーされるタイミングと場所を制御するには、ref(path) を呼び出してパスを指定します。オプションとして instance('INSTANCE_NAME') を使用してデータベース インスタンスを指定することもできます。インスタンスを指定しない場合、関数は Firebase プロジェクトのデフォルトのデータベース インスタンスにデプロイされます。次に例を示します。

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

このメソッドは、データベース インスタンス内の特定のパスで書き込みを処理するよう関数に指示します。パスを指定すると、そのパスに関係するすべての書き込みが対象となり、これにはそのパス下のあらゆる場所で発生する書き込みが含まれます。関数のパスを /foo/bar として設定すると、次の場所のイベントはいずれもマッチ対象となります。

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

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

パス コンポーネントは、中かっこで囲むことでワイルドカードとして指定できます。ref('foo/{bar}')/foo のすべての子と一致します。このワイルドカード パス コンポーネントの値は、関数の EventContext.params オブジェクト内で使用できます。この例では、値は event.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();
      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 snapshot.ref.parent.child('uppercase').set(uppercase);
    });

ユーザー認証情報にアクセスする

関数をトリガーしたユーザーの情報(権限など)には、EventContext.authEventContext.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 プロパティを使用すると、イベントの発生前にデータベースに保存されていた内容を検査できます。before プロパティによって返される DataSnapshot では、すべてのメソッド(val()exists() など)が前の値を参照します。新しい値を再度読み取るには、元の 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);
    });

フィードバックを送信...

Firebase Realtime Database
ご不明な点がありましたら、Google のサポートページをご覧ください。