Firebase forWebについて知る

1。概要

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

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

学習内容

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

必要なもの

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

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

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

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

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

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

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

3. イベント情報の編集

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

開始するには、StackBlitz インターフェースにもう少し慣れましょう。

  1. StackBlitz で、 index.htmlファイルを開きます。
  2. event-details-containerdescription-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 のメール ログインを有効にする

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

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

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

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

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

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

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

Cloud Firestore をセットアップする

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

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

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

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

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

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

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

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

  5. [完了]をクリックします。

5. Firebase を追加して構成する

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

Firebase ライブラリを追加する

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

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

このアプリを構築するには、Firebase Authentication、FirebaseUI、および Cloud Firestore ライブラリを使用します。この Codelab では、次の import ステートメントが既に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. プロジェクトの概要ページの中央にあるウェブ アイコンをクリックします。 ウェブアプリのアイコン新しい Firebase Web アプリを作成します。

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

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

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

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

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

  7. [Continue to console]をクリックします。Firebase 構成オブジェクトをアプリに追加します。
  8. StackBlitz に戻り、 index.jsファイルに移動します。
  9. Add Firebase project configuration object hereコメント行を見つけて、コメントのすぐ下に構成スニペットを貼り付けます。
  10. initializeApp関数呼び出しを追加して、独自の Firebase プロジェクト構成を使用して Firebase をセットアップします。
    // ...
    // 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インポート ステートメントを見つけ、次のようにgetAuthEmailAuthProviderを追加します。
    // ...
    // Add the Firebase products and methods that you want to use
    import { getAuth, EmailAuthProvider } from 'firebase/auth';
    
    import {} from 'firebase/firestore';
    
  3. 次のように、 initializeAppの直後に auth オブジェクトへの参照を保存します:
    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 を追加します。

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

    index.htmlファイルには、ID がfirebaseui-auth-containerのコンテナーがあることに注意してください。これは、ログインを保持するために 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] ボタンをクリックしてアプリにサインインします。
    • この Codelab では、メールの確認手順を設定していないため、偽のメール アドレスであっても、任意のメール アドレスを使用できます。
    • auth/operation-not-allowedまたはThe given sign-in provider is disabled for this Firebase projectエラー メッセージが表示された場合は、Firebase コンソールでサインイン プロバイダーとしてメール/パスワードが有効になっていることを確認してください。
    アプリのプレビュー

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

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

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

UI に認証状態を追加する

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

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

  1. StackBlitz で、 index.jsファイルに移動します。
  2. 上部で、 firebase/authインポート ステートメントを見つけ、次のようにsignOutonAuthStateChanged追加します。
    // ...
    // Add the Firebase products and methods that you want to use
    import {
      getAuth,
      EmailAuthProvider,
      signOut,
      onAuthStateChanged
    } from 'firebase/auth';
    
    import {} from 'firebase/firestore';
    
  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>
    
    <!-- ... -->
    

アプリのプレビュー

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

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

  1. StackBlitz で、 index.jsファイルに移動します。
  2. 上部で、 firebase/firestoreインポート ステートメントを見つけて、次のようにgetFirestoreaddDoc 、およびcollectionを追加します。
    // ...
    
    // 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';
    
  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 セキュリティ ルールで保護する必要があります。 (セキュリティ ルールの詳細については、Codelab の後半で説明します)。

  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. 「Hey there!」などのメッセージを入力し、 [送信]をクリックします。

このアクションにより、メッセージが 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 ステートメントを見つけて、次のようにqueryorderBy 、およびonSnapshotを追加します。
    // ...
    import {
      getFirestore,
      addDoc,
      collection,
      query,
      orderBy,
      onSnapshot
    } from 'firebase/firestore';
    
  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リスナーは、後でスナップショット リスナーをキャンセルするために使用できる登録解除関数を返します
    // ...
    // 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. ボーナスステップ: 学んだことを実践する

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

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

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

  1. StackBlitz で、 index.htmlファイルに移動します。
  2. guestbook-containerに、次のようにYESボタンとNOボタンのセットを追加します:
    <!-- ... -->
      <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>
    <!-- ... -->
    

アプリのプレビュー

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

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

  1. StackBlitz で、 index.jsファイルに移動します。
  2. 上部で、 firebase/firestoreインポート ステートメントを見つけて、次のようにdocsetDoc 、およびwhereを追加します。
    // ...
    // Add the Firebase products and methods that you want to use
    import {
      getFirestore,
      addDoc,
      collection,
      query,
      orderBy,
      onSnapshot,
      doc,
      setDoc,
      where
    } from 'firebase/firestore';
    
  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();
          // Subcribe 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 を使用してインタラクティブなリアルタイム ウェブ アプリケーションを構築しました。

カバーした内容

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

次のステップ

もっと詳しく知る

どうだった?

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