ウェブ版 Firebase について知る

1。概要

このコードラボでは、インタラクティブな Web アプリケーションを作成するためのFirebaseの基本のいくつかを学びます。いくつかの Firebase 製品を使用して、イベントの出欠確認とゲストブック チャット アプリを構築します。

このステップのスクリーンショット

学べること

  • Firebase Authentication と FirebaseUI を使用してユーザーを認証します。
  • Cloud Firestore を使用してデータを同期します。
  • データベースを保護するために Firebase セキュリティ ルールを作成します。

必要なもの

  • Chrome などの任意のブラウザ。
  • stackblitz.comにアクセスします (アカウントやサインインは必要ありません)。
  • Gmail アカウントのような Google アカウント。 GitHub アカウントに既に使用している電子メール アカウントをお勧めします。これにより、StackBlitz の高度な機能を使用できるようになります。
  • コードラボのサンプルコード。コードを取得する方法については、次のステップを参照してください。

2. 開始コードを取得する

このコードラボでは、複数の Firebase ワークフローが統合されたオンライン エディターであるStackBlitzを使用してアプリを構築します。 Stackblitz にはソフトウェアのインストールや特別な StackBlitz アカウントは必要ありません。

StackBlitz を使用すると、他のユーザーとプロジェクトを共有できます。あなたの StackBlitz プロジェクト URL を知っている他の人は、あなたのコードを見てプロジェクトをフォークすることができますが、StackBlitz プロジェクトを編集することはできません。

  1. 開始コードについては、次の URL にアクセスしてください: https://stackblitz.com/edit/firebase-gtk-web-start
  2. StackBlitz ページの上部で[フォーク]をクリックします。

このステップのスクリーンショット

これで、開始コードのコピーが独自の StackBlitz プロジェクトとして作成され、一意の名前と一意の URL が付けられました。すべてのファイルと変更は、この StackBlitz プロジェクトに保存されます。

3. イベント情報の編集

このコードラボの開始資料は、アプリのいくつかのスタイルシートやいくつかの HTML コンテナなど、Web アプリの構造を提供します。このコードラボの後半では、これらのコンテナを Firebase に接続します。

まず、StackBlitz インターフェイスについてもう少し詳しく見てみましょう。

  1. StackBlitz で、 index.htmlファイルを開きます。
  2. event-details-containerおよびdescription-containerを見つけて、イベントの詳細をいくつか編集してみます。

テキストを編集すると、StackBlitz のページが自動的にリロードされ、新しいイベントの詳細が表示されます。クールですね?

<!-- ... -->

<div id="app">
  <img src="..." />

  <section id="event-details-container">
     <h1>Firebase Meetup</h1>

     <p><i class="material-icons">calendar_today</i> October 30</p>
     <p><i class="material-icons">location_city</i> San Francisco</p>

  </section>

  <hr>

  <section id="firebaseui-auth-container"></section>

  <section id="description-container">
     <h2>What we'll be doing</h2>
     <p>Join us for a day full of Firebase Workshops and Pizza!</p>
  </section>
</div>

<!-- ... -->

アプリのプレビューは次のようになります。

アプリのプレビュー

このステップのスクリーンショット

4. Firebase プロジェクトを作成して設定する

イベント情報を表示することはゲストにとっては有益ですが、イベントを表示するだけでは誰にとってもあまり役に立ちません。このアプリにいくつかの動的な機能を追加してみましょう。このためには、Firebase をアプリに接続する必要があります。 Firebase を使い始めるには、Firebase プロジェクトを作成して設定する必要があります。

Firebaseプロジェクトを作成する

  1. Firebaseにサインインします。
  2. Firebase コンソールで、 [プロジェクトの追加] (または[プロジェクトの作成]) をクリックし、Firebase プロジェクトにFirebase-Web-Codelabという名前を付けます。

    このステップのスクリーンショット

  3. プロジェクト作成オプションをクリックして進みます。プロンプトが表示されたら、Firebase の利用規約に同意します。このアプリでは Analytics を使用しないため、Google Analytics 画面で [有効にしない] をクリックします。

Firebase プロジェクトの詳細については、 「Firebase プロジェクトを理解する」を参照してください。

コンソールで Firebase 製品を有効にして設定する

構築しているアプリは、ウェブアプリで利用できるいくつかの Firebase 製品を使用しています。

  • Firebase AuthenticationFirebase UIにより、ユーザーがアプリに簡単にサインインできるようになります。
  • Cloud Firestore は、構造化データをクラウド上に保存し、データが変更されたときに即座に通知を受け取ります。
  • データベースを保護するためのFirebase セキュリティ ルール

これらの製品の中には、特別な構成が必要な場合や、Firebase コンソールを使用して有効にする必要があるものもあります。

Firebase Authentication のメール サインインを有効にする

ユーザーが Web アプリにサインインできるようにするには、このコードラボではメール/パスワードによるサインイン方法を使用します。

  1. Firebase コンソールの左側のパネルで、 [ビルド] > [認証]をクリックします。次に、 「開始する」をクリックします。これで、認証ダッシュボードが表示され、サインアップしたユーザーの表示、サインイン プロバイダーの構成、および設定の管理を行うことができます。

    このステップのスクリーンショット

  2. [サインイン方法]タブを選択します (または、ここをクリックしてタブに直接移動します)。

    このステップのスクリーンショット

  3. プロバイダー オプションから[電子メール/パスワード]をクリックし、スイッチを[有効]に切り替えて、 [保存]をクリックします。

    このステップのスクリーンショット

Cloud Firestore をセットアップする

このウェブアプリはCloud Firestoreを使用してチャット メッセージを保存し、新しいチャット メッセージを受信します。

Cloud Firestore を設定する方法は次のとおりです。

  1. Firebase コンソールの左側のパネルで、 [構築] > [Firestore データベース]をクリックします。次に、 「データベースの作成」をクリックします。
  2. [データベースの作成]をクリックします。

    このステップのスクリーンショット

  3. 「テストモードで開始」オプションを選択します。セキュリティ ルールに関する免責事項をお読みください。テスト モードでは、開発中にデータベースに自由に書き込むことができます。 「次へ」をクリックします。

    このステップのスクリーンショット

  4. データベースの場所を選択します (デフォルトをそのまま使用できます)。ただし、この場所は後で変更できないことに注意してください。

    このステップのスクリーンショット

  5. 「完了」をクリックします。

5. Firebaseの追加と構成

Firebase プロジェクトを作成し、いくつかのサービスを有効にしたので、コードに Firebase を使用することと、どの Firebase プロジェクトを使用するかを指示する必要があります。

Firebase ライブラリを追加する

アプリで Firebase を使用するには、Firebase ライブラリをアプリに追加する必要があります。 Firebase のドキュメント で説明されているように、これを行うには複数の方法があります。たとえば、Google の CDN からライブラリを追加することも、Browserify を使用している場合は、npm を使用してライブラリをローカルにインストールし、アプリにパッケージ化することもできます。

StackBlitz は自動バンドルを提供するため、import ステートメントを使用して Firebase ライブラリを追加できます。モジュール (v9) バージョンのライブラリを使用します。これは、「ツリー シェーキング」と呼ばれるプロセスを通じて Web ページ全体のサイズを縮小するのに役立ちます。モジュラー SDK の詳細については、ドキュメントを参照してください。

このアプリを構築するには、Firebase Authentication、FirebaseUI、Cloud Firestore ライブラリを使用します。このコードラボでは、次のインポート ステートメントがindex.jsファイルの先頭にすでに含まれており、各 Firebase ライブラリからさらにメソッドをインポートしていきます。

// Import stylesheets
import './style.css';

// Firebase App (the core Firebase SDK) is always required
import { initializeApp } from 'firebase/app';

// Add the Firebase products and methods that you want to use
import {} from 'firebase/auth';
import {} from 'firebase/firestore';

import * as firebaseui from 'firebaseui';

Firebase ウェブアプリを Firebase プロジェクトに追加する

  1. Firebase コンソールに戻り、左上の [プロジェクトの概要] をクリックしてプロジェクトの概要ページに移動します。
  2. プロジェクトの概要ページの中央にある Web アイコンをクリックします。 ウェブアプリのアイコン新しい Firebase ウェブアプリを作成します。

    このステップのスクリーンショット

  3. アプリをWeb Appというニックネームで登録します。
  4. このコードラボでは、 [このアプリの Firebase Hosting も設定する]の横のボックスをオンにしないでください。ここでは、StackBlitz のプレビュー ペインを使用します。
  5. [アプリの登録]をクリックします。

    このステップのスクリーンショット

  6. Firebase 構成オブジェクトをクリップボードにコピーします。

    このステップのスクリーンショット

  7. [コンソールに進む]をクリックします。Firebase 構成オブジェクトをアプリに追加します。
  8. StackBlitz に戻り、 index.jsファイルに移動します。
  9. Add Firebase project configuration object hereというコメント行を見つけて、そのコメントのすぐ下に設定スニペットを貼り付けます。
  10. 独自の Firebase プロジェクト構成を使用して Firebase をセットアップするには、 initializeApp関数呼び出しを追加します。
    // ...
    // Add Firebase project configuration object here
    const firebaseConfig = {
      apiKey: "random-unique-string",
      authDomain: "your-projectId.firebaseapp.com",
      databaseURL: "https://your-projectId.firebaseio.com",
      projectId: "your-projectId",
      storageBucket: "your-projectId.appspot.com",
      messagingSenderId: "random-unique-string",
      appId: "random-unique-string",
    };
    
    // Initialize Firebase
    initializeApp(firebaseConfig);
    

6. ユーザーサインイン (RSVP) を追加します。

アプリに Firebase を追加したので、 Firebase Authenticationを使用してユーザーを登録する RSVP ボタンを設定できます。

電子メール サインインと FirebaseUI を使用してユーザーを認証する

ユーザーに電子メール アドレスでのサインインを求める [RSVP] ボタンが必要です。これを行うには、 FirebaseUI をRSVP ボタンに接続します。FirebaseUI は、Firebase Auth 上に事前構築された UI を提供するライブラリです。

FirebaseUI には、次の 2 つのことを行う構成 (ドキュメントのオプションを参照) が必要です。

  • FirebaseUI にメール/パスワードによるサインイン方法を使用するように指示します。
  • サインインが成功した場合のコールバックを処理し、リダイレクトを回避するために false を返します。シングルページの Web アプリを構築しているため、ページを更新したくないとします。

FirebaseUI Auth を初期化するコードを追加します

  1. StackBlitz で、 index.jsファイルに移動します。
  2. 上部でfirebase/auth import ステートメントを見つけて、
    // ...
    // Add the Firebase products and methods that you want to use
    import { getAuth, EmailAuthProvider } from 'firebase/auth';
    
    import {} from 'firebase/firestore';
    
    のようにgetAuthEmailAuthProviderを追加します。
  3. 次のように、 initializeAppの直後に認証オブジェクトへの参照を保存します:
    initializeApp(firebaseConfig);
    auth = getAuth();
    
  4. FirebaseUI 構成が開始コードですでに提供されていることに注意してください。電子メール認証プロバイダーを使用するようにすでにセットアップされています。
  5. index.jsmain()関数の最後に、次のように FirebaseUI 初期化ステートメントを追加します。
    async function main() {
      // ...
    
      // Initialize the FirebaseUI widget using Firebase
      const ui = new firebaseui.auth.AuthUI(auth);
    }
    main();
    
    

HTML に RSVP ボタンを追加する

  1. StackBlitz で、 index.htmlファイルに移動します。
  2. 以下の例に示すようにevent-details-container内に RSVP ボタンの HTML を追加します。

    このコードラボでは、 index.jsファイルにこれらの特定の ID のフックがすでに存在するため、以下に示すように同じid値を使用するように注意してください。

    index.htmlファイルには、 firebaseui-auth-container ID を持つコンテナーがあることに注意してください。これは、ログインを保持するために FirebaseUI に渡す ID です。
    <!-- ... -->
    
    <section id="event-details-container">
        <!-- ... -->
        <!-- ADD THE RSVP BUTTON HERE -->
        <button id="startRsvp">RSVP</button>
    </section>
    <hr>
    <section id="firebaseui-auth-container"></section>
    <!-- ... -->
    
    アプリのプレビュー

    このステップのスクリーンショット

  3. RSVP ボタンにリスナーを設定し、FirebaseUI 開始関数を呼び出します。これにより、サインイン ウィンドウを表示したいことが FirebaseUI に伝えられます。

    次のコードを、 index.jsmain()関数の最後に追加します。
    async function main() {
      // ...
    
      // Listen to RSVP button clicks
      startRsvpButton.addEventListener("click",
       () => {
            ui.start("#firebaseui-auth-container", uiConfig);
      });
    }
    main();
    

アプリへのサインインをテストする

  1. StackBlitz のプレビュー ウィンドウで、[RSVP] ボタンをクリックしてアプリにサインインします。
    • このコードラボでは電子メール検証手順を設定していないため、偽の電子メール アドレスであっても、任意の電子メール アドレスを使用できます。
    • auth/operation-not-allowedまたはThe given sign-in provider is disabled for this Firebase projectエラー メッセージが表示された場合は、Firebase コンソールでサインイン プロバイダーとしてメール/パスワードが有効になっていることを確認してください。
    アプリのプレビュー

    このステップのスクリーンショット

  2. Firebase コンソールの認証ダッシュボードに移動します。 [ユーザー]タブに、アプリにサインインするために入力したアカウント情報が表示されます。

    このステップのスクリーンショット

認証状態を UI に追加する

次に、サインインしていることが UI に反映されていることを確認します。

Firebase Authentication 状態リスナー コールバックを使用します。これは、ユーザーのサインイン ステータスが変化するたびに通知を受け取ります。現在サインインしているユーザーがいる場合、アプリは「出欠確認」ボタンを「ログアウト」ボタンに切り替えます。

  1. StackBlitz で、 index.jsファイルに移動します。
  2. 上部でfirebase/auth import ステートメントを見つけて、
    // ...
    // Add the Firebase products and methods that you want to use
    import {
      getAuth,
      EmailAuthProvider,
      signOut,
      onAuthStateChanged
    } from 'firebase/auth';
    
    import {} from 'firebase/firestore';
    
    のようにsignOutonAuthStateChanged追加します。
  3. main()関数の最後に次のコードを追加します:
    async function main() {
      // ...
    
      // Listen to the current Auth state
      onAuthStateChanged(auth, user => {
        if (user) {
          startRsvpButton.textContent = 'LOGOUT';
        } else {
          startRsvpButton.textContent = 'RSVP';
        }
      });
    }
    main();
    
  4. ボタン リスナーで、現在のユーザーが存在するかどうかを確認し、ログアウトします。これを行うには、現在のstartRsvpButton.addEventListener次のものに置き換えます:
    // ...
    // Called when the user clicks the RSVP button
    startRsvpButton.addEventListener('click', () => {
      if (auth.currentUser) {
        // User is signed in; allows user to sign out
        signOut(auth);
      } else {
        // No user is signed in; allows user to sign in
        ui.start('#firebaseui-auth-container', uiConfig);
      }
    });
    

これで、アプリのボタンに[LOGOUT]が表示され、クリックされると[RSVP]に戻るはずです。

アプリのプレビュー

このステップのスクリーンショット

7. Cloud Firestore にメッセージを書き込む

ユーザーが来ることがわかっているのは素晴らしいことですが、アプリ内でゲストに何か他のことをしてもらいましょう。ゲストブックにメッセージを残せたらどうなるでしょうか?なぜ来ることに興奮しているのか、誰に会いたいのかを共有できます。

ユーザーがアプリに書いたチャット メッセージを保存するには、 Cloud Firestore を使用します。

データ・モデル

Cloud Firestore は NoSQL データベースであり、データベースに保存されるデータはコレクション、ドキュメント、フィールド、サブコレクションに分割されます。チャットの各メッセージを、 guestbookと呼ばれる最上位のコレクションにドキュメントとして保存します。

複数のメッセージ ドキュメントを含むゲストブック コレクションを示す Firestore データ モデルのグラフィック

Firestore にメッセージを追加する

このセクションでは、ユーザーがデータベースに新しいメッセージを書き込むための機能を追加します。まず、UI 要素 (メッセージ フィールドと送信ボタン) の HTML を追加します。次に、これらの要素をデータベースにフックするコードを追加します。

メッセージ フィールドと送信ボタンの UI 要素を追加するには:

  1. StackBlitz で、 index.htmlファイルに移動します。
  2. guestbook-containerを見つけて、次の HTML を追加して、メッセージ入力フィールドと送信ボタンを備えたフォームを作成します。
    <!-- ... -->
    
     <section id="guestbook-container">
       <h2>Discussion</h2>
    
       <form id="leave-message">
         <label>Leave a message: </label>
         <input type="text" id="message">
         <button type="submit">
           <i class="material-icons">send</i>
           <span>SEND</span>
         </button>
       </form>
    
     </section>
    
    <!-- ... -->
    

アプリのプレビュー

このステップのスクリーンショット

ユーザーが[SEND]ボタンをクリックすると、以下のコード スニペットがトリガーされます。メッセージ入力フィールドの内容をデータベースのguestbookコレクションに追加します。具体的には、 addDocメソッドは、 guestbookコレクションの新しいドキュメント (自動生成された ID を持つ) にメッセージ コンテンツを追加します。

  1. StackBlitz で、 index.jsファイルに移動します。
  2. 上部でfirebase/firestore import ステートメントを見つけて、
    // ...
    
    // Add the Firebase products and methods that you want to use
    import {
      getAuth,
      EmailAuthProvider,
      signOut,
      onAuthStateChanged
    } from 'firebase/auth';
    
    import {
      getFirestore,
      addDoc,
      collection
    } from 'firebase/firestore';
    
    のようにgetFirestoreaddDoccollectionを追加します。
  3. 次に、 initializeApp直後に Firestore dbオブジェクトへの参照を保存します:
    initializeApp(firebaseConfig);
    auth = getAuth();
    db = getFirestore();
    
  4. main()関数の最後に、次のコードを追加します。

    auth.currentUser.uid 、Firebase Authentication がログインしているすべてのユーザーに与える自動生成された一意の ID への参照であることに注意してください。
    async function main() {
      // ...
    
      // Listen to the form submission
      form.addEventListener('submit', async e => {
        // Prevent the default form redirect
        e.preventDefault();
        // Write a new message to the database collection "guestbook"
        addDoc(collection(db, 'guestbook'), {
          text: input.value,
          timestamp: Date.now(),
          name: auth.currentUser.displayName,
          userId: auth.currentUser.uid
        });
        // clear message input field
        input.value = '';
        // Return false to avoid redirect
        return false;
      });
    }
    main();
    

サインインしているユーザーにのみゲストブックを表示する

ゲストのチャットを誰にでも見られるようにする必要はありません。チャットを保護するためにできることの 1 つは、サインインしているユーザーのみにゲストブックの表示を許可することです。ただし、独自のアプリの場合は、 Firebase セキュリティ ルールを使用してデータベースを保護することも必要になります。 (セキュリティ ルールの詳細については、コードラボで後ほど説明します。)

  1. StackBlitz で、 index.jsファイルに移動します。
  2. onAuthStateChangedリスナーを編集して、ゲストブックを表示または非表示にします。
    // ...
    
    // Listen to the current Auth state
    onAuthStateChanged(auth, user => {
      if (user) {
        startRsvpButton.textContent = 'LOGOUT';
        // Show guestbook to logged-in users
        guestbookContainer.style.display = 'block';
      } else {
        startRsvpButton.textContent = 'RSVP';
        // Hide guestbook for non-logged-in users
        guestbookContainer.style.display = 'none';
      }
    });
    

メッセージ送信のテスト

  1. アプリにサインインしていることを確認してください。
  2. 「こんにちは!」などのメッセージを入力し、 [送信]をクリックします。

このアクションにより、メッセージが Cloud Firestore データベースに書き込まれます。ただし、データの取得を実装する必要があるため、実際の Web アプリではメッセージはまだ表示されません。次にそれを行います。

ただし、新しく追加されたメッセージは Firebase コンソールで確認できます。

Firebase コンソールのFirestore データベースダッシュボードに、新しく追加したメッセージを含むguestbookコレクションが表示されます。メッセージを送信し続けると、ゲストブック コレクションには次のような多くのドキュメントが含まれるようになります。

Firebaseコンソール

このステップのスクリーンショット

8. メッセージを読む

メッセージを同期する

ゲストがデータベースにメッセージを書き込めるのは素晴らしいことですが、アプリではまだ見ることができません。

メッセージを表示するには、データ変更時にトリガーするリスナーを追加し、新しいメッセージを表示する UI 要素を作成する必要があります。

アプリから新しく追加されたメッセージをリッスンするコードを追加します。まず、HTML にメッセージを表示するセクションを追加します。

  1. StackBlitz で、 index.htmlファイルに移動します。
  2. guestbook-containerに、 guestbookの ID を持つ新しいセクションを追加します。
    <!-- ... -->
    
      <section id="guestbook-container">
       <h2>Discussion</h2>
    
       <form><!-- ... --></form>
    
       <section id="guestbook"></section>
    
     </section>
    
    <!-- ... -->
    

次に、データに加えられた変更をリッスンするリスナーを登録します。

  1. StackBlitz で、 index.jsファイルに移動します。
  2. 上部でfirebase/firestore import ステートメントを見つけて、
    // ...
    import {
      getFirestore,
      addDoc,
      collection,
      query,
      orderBy,
      onSnapshot
    } from 'firebase/firestore';
    
    のようにqueryorderBy 、およびonSnapshotを追加します。
  3. main()関数の最後に、データベース内のすべてのドキュメント (ゲストブック メッセージ) をループする次のコードを追加します。このコードで何が起こっているかについて詳しくは、スニペットの下にある情報をお読みください。
    async function main() {
      // ...
    
      // Create query for messages
      const q = query(collection(db, 'guestbook'), orderBy('timestamp', 'desc'));
      onSnapshot(q, snaps => {
        // Reset page
        guestbook.innerHTML = '';
        // Loop through documents in database
        snaps.forEach(doc => {
          // Create an HTML entry for each document and add it to the chat
          const entry = document.createElement('p');
          entry.textContent = doc.data().name + ': ' + doc.data().text;
          guestbook.appendChild(entry);
        });
      });
    }
    main();
    

データベース内のメッセージをリッスンするには、 collection関数を使用して特定のコレクションに対するクエリを作成しました。上記のコードは、チャット メッセージが保存されているguestbookコレクション内の変更をリッスンします。また、メッセージは日付順に並べられ、 orderBy('timestamp', 'desc')を使用して最新のメッセージが先頭に表示されます。

onSnapshot関数は、使用するクエリとコールバック関数の 2 つのパラメータを取ります。コールバック関数は、クエリに一致するドキュメントに変更があったときにトリガーされます。これは、メッセージが削除、変更、または追加された場合に発生する可能性があります。詳細については、 Cloud Firestore のドキュメントを参照してください。

メッセージの同期をテストする

Cloud Firestore は、データベースに登録しているクライアントとデータを自動的かつ即座に同期します。

  • データベース内に以前に作成したメッセージがアプリに表示されるはずです。気軽に新しいメッセージを書いてください。それらは即座に表示されるはずです。
  • ワークスペースを複数のウィンドウまたはタブで開くと、メッセージはタブ間でリアルタイムに同期されます。
  • (オプション) Firebase コンソールの[データベース]セクションで直接、手動でメッセージを削除、変更、または新しいメッセージを追加してみることができます。変更があれば UI に表示されるはずです。

おめでとう!アプリで Cloud Firestore ドキュメントを読んでいます。

アプリのプレビュー

このステップのスクリーンショット

9. 基本的なセキュリティ ルールを設定する

最初にテスト モードを使用するように Cloud Firestore を設定します。これは、データベースが読み取りと書き込みのために開いていることを意味します。ただし、テスト モードは開発の非常に初期段階でのみ使用してください。ベスト プラクティスとして、アプリの開発時にデータベースのセキュリティ ルールを設定する必要があります。セキュリティはアプリの構造と動作に不可欠である必要があります。

セキュリティ ルールを使用すると、データベース内のドキュメントやコレクションへのアクセスを制御できます。柔軟なルール構文を使用すると、データベース全体へのすべての書き込みから特定のドキュメントの操作まで、あらゆるものに一致するルールを作成できます。

Firebase コンソールで Cloud Firestore のセキュリティ ルールを作成できます。

  1. Firebase コンソールの[ビルド]セクションで、 [Firestore データベース]をクリックし、 [ルール]タブを選択します (または、ここをクリックして[ルール]タブに直接移動します)。
  2. 次のデフォルトのセキュリティ ルールが表示されます。パブリック アクセスの時間制限は今日から数週間です。

このステップのスクリーンショット

コレクションを識別する

まず、アプリがデータを書き込むコレクションを特定します。

  1. 既存のmatch /{document=**}句を削除すると、ルールは次のようになります:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
      }
    }
    
  2. match /databases/{database}/documentsで、保護するコレクションを指定します:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /guestbook/{entry} {
         // You'll add rules here in the next step.
      }
    }
    

セキュリティルールを追加する

各ゲストブック ドキュメントのフィールドとして認証 UID を使用したため、認証 UID を取得して、ドキュメントに書き込もうとしているユーザーが一致する認証 UID を持っているかどうかを確認できます。

  1. 以下に示すように、読み取りおよび書き込みルールをルール セットに追加します:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /guestbook/{entry} {
          allow read: if request.auth.uid != null;
          allow create:
            if request.auth.uid == request.resource.data.userId;
        }
      }
    }
    
  2. [公開]をクリックして、新しいルールを展開します。これで、ゲストブックの場合、サインインしたユーザーのみがメッセージ (どのメッセージでも!) を読むことができますが、メッセージを作成できるのはユーザー ID を使用することだけです。また、メッセージの編集や削除も許可されていません。

検証ルールを追加する

  1. データ検証を追加して、予期されるすべてのフィールドがドキュメント内に存在することを確認します:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        match /guestbook/{entry} {
          allow read: if request.auth.uid != null;
          allow create:
          if request.auth.uid == request.resource.data.userId
              && "name" in request.resource.data
              && "text" in request.resource.data
              && "timestamp" in request.resource.data;
        }
      }
    }
    
  2. 「公開」をクリックして、新しいルールをデプロイします。

リスナーをリセットする

アプリでは認証されたユーザーのみがログインを許可されるようになったため、ゲストブックfirestoreクエリを認証リスナー内に移動する必要があります。そうしないと、権限エラーが発生し、ユーザーがログアウトするとアプリが切断されます。

  1. StackBlitz で、 index.jsファイルに移動します。
  2. ゲストブック コレクションonSnapshotリスナーをsubscribeGuestbookという新しい関数にプルします。また、 onSnapshot関数の結果をguestbookListener変数に代入します。

    Firestore onSnapshotリスナーは、後でスナップショット リスナーをキャンセルするために使用できる unsubscribe 関数を返します
    // ...
    // Listen to guestbook updates
    function subscribeGuestbook() {
      const q = query(collection(db, 'guestbook'), orderBy('timestamp', 'desc'));
      guestbookListener = onSnapshot(q, snaps => {
        // Reset page
        guestbook.innerHTML = '';
        // Loop through documents in database
        snaps.forEach(doc => {
          // Create an HTML entry for each document and add it to the chat
          const entry = document.createElement('p');
          entry.textContent = doc.data().name + ': ' + doc.data().text;
          guestbook.appendChild(entry);
        });
      });
    }
    
  3. unsubscribeGuestbookという新しい関数をその下に追加します。 guestbookListener変数が null でないかどうかを確認し、関数を呼び出してリスナーをキャンセルします。
    // ...
    // Unsubscribe from guestbook updates
    function unsubscribeGuestbook() {
      if (guestbookListener != null) {
        guestbookListener();
        guestbookListener = null;
      }
    }
    

最後に、新しい関数をonAuthStateChangedコールバックに追加します。

  1. if (user)の最後にsubscribeGuestbook()を追加します。
  2. elseステートメントの最後にunsubscribeGuestbook()を追加します。
    // ...
    // Listen to the current Auth state
    onAuthStateChanged(auth, user => {
      if (user) {
        startRsvpButton.textContent = 'LOGOUT';
        // Show guestbook to logged-in users
        guestbookContainer.style.display = 'block';
        // Subscribe to the guestbook collection
        subscribeGuestbook();
      } else {
        startRsvpButton.textContent = 'RSVP';
        // Hide guestbook for non-logged-in users
        guestbookContainer.style.display = 'none';
        // Unsubscribe from the guestbook collection
        unsubscribeGuestbook();
      }
    });
    

10. ボーナスステップ: 学んだことを実践する

出席者の出欠ステータスを記録する

現時点では、アプリでは、イベントに興味がある場合にのみチャットを開始できます。また、誰かが来ているかどうかを知る唯一の方法は、その人がチャットに投稿するかどうかです。準備を整えて、何人来るのかを人々に知らせましょう。

イベントに参加したい人を登録するためのトグルを追加し、参加者数を収集します。

  1. StackBlitz で、 index.htmlファイルに移動します。
  2. guestbook-containerに、
    <!-- ... -->
      <section id="guestbook-container">
       <h2>Are you attending?</h2>
         <button id="rsvp-yes">YES</button>
         <button id="rsvp-no">NO</button>
    
       <h2>Discussion</h2>
    
       <!-- ... -->
    
     </section>
    <!-- ... -->
    
    のように、 YESボタンとNOボタンのセットを追加します。

アプリのプレビュー

このステップのスクリーンショット

次に、ボタンクリック用のリスナーを登録します。ユーザーが[はい]をクリックした場合は、認証 UID を使用して応答をデータベースに保存します。

  1. StackBlitz で、 index.jsファイルに移動します。
  2. 上部でfirebase/firestore import ステートメントを見つけて、
    // ...
    // Add the Firebase products and methods that you want to use
    import {
      getFirestore,
      addDoc,
      collection,
      query,
      orderBy,
      onSnapshot,
      doc,
      setDoc,
      where
    } from 'firebase/firestore';
    
    のようにdocsetDoc 、およびwhereを追加します。
  3. main()関数の最後に、RSVP ステータスをリッスンするコード
    async function main() {
      // ...
    
      // Listen to RSVP responses
      rsvpYes.onclick = async () => {
      };
      rsvpNo.onclick = async () => {
      };
    }
    main();
    
    
    を追加します。
  4. 次に、 attendeesという新しいコレクションを作成し、いずれかの [RSVP] ボタンをクリックした場合にドキュメント参照を登録します。どのボタンがクリックされたかに応じて、その参照をtrueまたはfalseに設定します。

    まず、 rsvpYesの場合:
    // ...
    // Listen to RSVP responses
    rsvpYes.onclick = async () => {
      // Get a reference to the user's document in the attendees collection
      const userRef = doc(db, 'attendees', auth.currentUser.uid);
    
      // If they RSVP'd yes, save a document with attendi()ng: true
      try {
        await setDoc(userRef, {
          attending: true
        });
      } catch (e) {
        console.error(e);
      }
    };
    
    次に、 rsvpNoの場合も同じですが、値がfalseの場合:
    rsvpNo.onclick = async () => {
      // Get a reference to the user's document in the attendees collection
      const userRef = doc(db, 'attendees', auth.currentUser.uid);
    
      // If they RSVP'd yes, save a document with attending: true
      try {
        await setDoc(userRef, {
          attending: false
        });
      } catch (e) {
        console.error(e);
      }
    };
    

セキュリティ ルールを更新する

すでにいくつかのルールが設定されているため、ボタンを使用して追加する新しいデータは拒否されます。

attendeesコレクションへの追加を許可する

attendeesコレクションへの追加を許可するには、ルールを更新する必要があります。

  1. attendeesコレクションでは、ドキュメント名として認証 UID を使用しているため、それを取得して、送信者のuidが作成中のドキュメントと同じであることを確認できます。出席者リストを誰でも読み取ることができるようにします (そこにはプライベート データがないため)。ただし、作成者のみが出席者リストを更新できるようにする必要があります。
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        // ... //
        match /attendees/{userId} {
          allow read: if true;
          allow write: if request.auth.uid == userId;
        }
      }
    }
    
  2. 「公開」をクリックして、新しいルールをデプロイします。

検証ルールを追加する

  1. いくつかのデータ検証ルールを追加して、予期されるすべてのフィールドがドキュメント内に存在することを確認します:
    rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
        // ... //
        match /attendees/{userId} {
          allow read: if true;
          allow write: if request.auth.uid == userId
              && "attending" in request.resource.data;
    
        }
      }
    }
    
  2. ルールを展開するには、 [公開]をクリックすることを忘れないでください。

(オプション)ボタンをクリックした結果を表示できるようになりました。 Firebase コンソールで Cloud Firestore ダッシュボードに移動します。

RSVP ステータスの読み取り

応答を記録したので、誰が来ているかを確認し、それを UI に反映してみましょう。

  1. StackBlitz で、 index.htmlファイルに移動します。
  2. description-containerに、 number-attendingの ID を持つ新しい要素を追加します。
    <!-- ... -->
    
     <section id="description-container">
         <!-- ... -->
         <p id="number-attending"></p>
     </section>
    
    <!-- ... -->
    

次に、 attendeesコレクションのリスナーを登録し、 YES応答の数をカウントします。

  1. StackBlitz で、 index.jsファイルに移動します。
  2. main()関数の最後に、RSVP ステータスをリッスンしてYESクリックをカウントする次のコードを追加します。
    async function main() {
      // ...
    
      // Listen for attendee list
      const attendingQuery = query(
        collection(db, 'attendees'),
        where('attending', '==', true)
      );
      const unsubscribe = onSnapshot(attendingQuery, snap => {
        const newAttendeeCount = snap.docs.length;
        numberAttending.innerHTML = newAttendeeCount + ' people going';
      });
    }
    main();
    

最後に、現在のステータスに対応するボタンを強調表示しましょう。

  1. 現在の認証 UID がattendeesコレクションにエントリがあるかどうかを確認する関数を作成し、ボタン クラスをclickedに設定します。
    // ...
    // Listen for attendee list
    function subscribeCurrentRSVP(user) {
      const ref = doc(db, 'attendees', user.uid);
      rsvpListener = onSnapshot(ref, doc => {
        if (doc && doc.data()) {
          const attendingResponse = doc.data().attending;
    
          // Update css classes for buttons
          if (attendingResponse) {
            rsvpYes.className = 'clicked';
            rsvpNo.className = '';
          } else {
            rsvpYes.className = '';
            rsvpNo.className = 'clicked';
          }
        }
      });
    }
    
  2. また、購読を解除する機能も作りましょう。これはユーザーがログアウトするときに使用されます。
    // ...
    function unsubscribeCurrentRSVP() {
      if (rsvpListener != null) {
        rsvpListener();
        rsvpListener = null;
      }
      rsvpYes.className = '';
      rsvpNo.className = '';
    }
    
  3. 認証リスナーから関数を呼び出します。
    // ...
    // Listen to the current Auth state
      // Listen to the current Auth state
      onAuthStateChanged(auth, user => {
        if (user) {
          startRsvpButton.textContent = 'LOGOUT';
          // Show guestbook to logged-in users
          guestbookContainer.style.display = 'block';
    
          // Subscribe to the guestbook collection
          subscribeGuestbook();
          // Subscribe to the user's RSVP
          subscribeCurrentRSVP(user);
        } else {
          startRsvpButton.textContent = 'RSVP';
          // Hide guestbook for non-logged-in users
          guestbookContainer.style.display = 'none'
          ;
          // Unsubscribe from the guestbook collection
          unsubscribeGuestbook();
          // Unsubscribe from the guestbook collection
          unsubscribeCurrentRSVP();
        }
      });
    
  4. 複数のユーザーとしてログインして、 「はい」ボタンをクリックするたびにカウントが増加することを確認してください。

アプリのプレビュー

このステップのスクリーンショット

11. おめでとうございます!

Firebase を使用して、インタラクティブなリアルタイム Web アプリケーションを構築しました。

私たちがカバーした内容

  • Firebase認証
  • FirebaseUI
  • クラウドファイアストア
  • Firebase セキュリティ ルール

次のステップ

もっと詳しく知る

どうだった?

ぜひフィードバックをお待ちしております。こちらの(非常に)短いフォームにご記入ください。