AngularFire ウェブ Codelab

1. 概要

この Codelab では、Firebase のプロダクトとサービスを使用してチャット クライアントを実装、デプロイし、AngularFire を使用してウェブ アプリケーションを作成する方法を学びます。

angularfire-2.png

学習内容

  • Angular と Firebase を使用してウェブアプリを作成します。
  • Cloud Firestore と Cloud Storage for Firebase を使用してデータを同期する。
  • Firebase Authentication を使用してユーザーを認証します。
  • Firebase Hosting を使用してウェブアプリをデプロイする。
  • Firebase Cloud Messaging を使用して通知を送信する。
  • ウェブアプリのパフォーマンス データを収集します。

必要なもの

  • 任意の IDE またはテキスト エディタ(WebStormAtomSublimeVS Code など)
  • パッケージ マネージャーの npm(通常は Node.js に付属)
  • ターミナル/コンソール
  • 任意のブラウザ(Chrome など)
  • Codelab のサンプルコード(コードを取得する方法については、Codelab の次のステップをご覧ください)

2. サンプルコードを取得する

コマンドラインから、Codelab の GitHub リポジトリのクローンを作成します。

git clone https://github.com/firebase/codelab-friendlychat-web

Git がインストールされていない場合は、リポジトリを ZIP ファイルとしてダウンロードすることもできます。

スターター アプリをインポートする

IDE を使用して、クローンを作成したリポジトリから Ұ angularfire-start ディレクトリを開くかインポートします。この 🙁? angularfire-start ディレクトリには、完全に機能するチャット ウェブアプリとなる Codelab の開始用コードが含まれています。

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

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

  1. Firebase にログインします。
  2. Firebase コンソールで [プロジェクトを追加] をクリックし、Firebase プロジェクトに「FriendlyChat」という名前を付けます。Firebase プロジェクトのプロジェクト ID を覚えておいてください。
  3. [このプロジェクトで Google アナリティクスを有効にする] チェックボックスをオフにします。
  4. [プロジェクトを作成] をクリックします。

これから構築するアプリケーションは、ウェブアプリで利用可能な Firebase プロダクトを使用します。

  • ユーザーがアプリに簡単にログインできるようにする Firebase Authentication
  • 構造化されたデータをクラウドに保存し、データが変更されたときに即座に通知を受け取る Cloud Firestore
  • ファイルをクラウドに保存する Cloud Storage for Firebase
  • アセットをホストして提供する Firebase Hosting
  • プッシュ通知を送信し、ブラウザのポップアップ通知を表示する Firebase Cloud Messaging
  • Firebase Performance Monitoring: アプリのユーザー パフォーマンス データを収集します。

これらのプロダクトの一部は、特別な構成が必要な場合や、Firebase コンソールを使用して有効にする必要があります。

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

  1. ウェブアイコン 58d6543a156e56f9.png をクリックして、新しい Firebase ウェブアプリを作成します。
  2. アプリを [Friendly Chat] というニックネームで登録し、[このアプリの Firebase Hosting も設定します] の横にあるチェックボックスをオンにし、[アプリを登録] をクリックします。
  3. 次のステップでは、構成オブジェクトが表示されます。JS オブジェクトだけ(周囲の HTML は除く)を firebase-config.js にコピーします。

ウェブアプリの登録のスクリーンショット

Firebase Authentication の Google ログインを有効にする

ユーザーが Google アカウントでウェブアプリにログインできるようにするには、Google のログイン方法を使用します。

Google ログインを有効にする必要があります。

  1. Firebase コンソールで、左側のパネルにある [ビルド] セクションを見つけます。
  2. [Authentication] をクリックし、[Sign-in method] タブをクリックします(またはこちらをクリックして、直接移動します)。
  3. Google ログイン プロバイダを有効にして、[保存] をクリックします。
  4. アプリの公開名を [Friendly Chat] に設定し、プルダウン メニューから [Project support email] を選択します。
  5. Google Cloud コンソールで OAuth 同意画面を構成し、ロゴを追加します。

d89fb3873b5d36ae.png

Cloud Firestore を有効にする

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

Cloud Firestore を有効にする必要があります。

  1. Firebase コンソールの [構築] セクションで、[Firestore データベース] をクリックします
  2. Cloud Firestore ペインで [データベースを作成] をクリックします。

729991a081e7cd5.png

  1. [テストモードで開始] オプションを選択し、セキュリティ ルールに関する免責条項を読み、[次へ] をクリックします。

テストモードを使用すると、開発中にデータベースに自由に書き込むことができます。この Codelab の後半で、データベースの安全性を高めます。

77e4986cbeaf9dee.png

  1. Cloud Firestore データを保存するロケーションを設定します。デフォルトのままにするか、近くのリージョンを選択します。[完了] をクリックして Firestore をプロビジョニングします。

9f2bb0d4e7ca49c7.png

Cloud Storage を有効にする

このウェブアプリは、Cloud Storage for Firebase を使用して画像を保存、アップロード、共有します。

Cloud Storage を有効にする必要があります。

  1. Firebase コンソールの [ビルド] セクションで、[Storage] をクリックします。
  2. [使ってみる] ボタンが表示されていない場合は、Cloud Storage はすでに有効になっているため、以下の手順を行う必要はありません。
  3. [利用開始] をクリックします。
  4. Firebase プロジェクトのセキュリティ ルールに関する免責条項を読み、[次へ] をクリックします。

デフォルトのセキュリティ ルールでは、認証されたユーザーであれば誰でも Cloud Storage に何でも書き込めます。この Codelab の後半では、ストレージのセキュリティを強化します。

62f1afdcd1260127.png

  1. Cloud Storage のロケーションは、Cloud Firestore データベース用に選択したのと同じリージョンで事前に選択されています。[完了] をクリックして設定を完了します。

1d7f49ebaddb32fc.png

4. Firebase コマンドライン インターフェースをインストールする

Firebase コマンドライン インターフェース(CLI)では、Firebase Hosting を使用してウェブアプリをローカルで提供したり、ウェブアプリを Firebase プロジェクトにデプロイしたりできます。

  1. 次の npm コマンドを実行して CLI をインストールします。
npm -g install firebase-tools
  1. 次のコマンドを実行して、CLI が正しくインストールされていることを確認します。
firebase --version

Firebase CLI のバージョンが v4.1.0 以降であることを確認します。

  1. 次のコマンドを実行して、Firebase CLI を承認します。
firebase login

アプリのローカル ディレクトリ(Codelab の前半でクローンを作成したリポジトリ)から Firebase Hosting のアプリの構成を pull するように、ウェブアプリ テンプレートを設定しました。ただし、構成を pull するには、アプリを Firebase プロジェクトに関連付ける必要があります。

  1. コマンドラインでアプリのローカル angularfire-start ディレクトリにアクセスしていることを確認します。
  2. 次のコマンドを実行して、アプリを Firebase プロジェクトに関連付けます。
firebase use --add
  1. プロンプトが表示されたら、プロジェクト ID を選択し、Firebase プロジェクトにエイリアスを指定します。

エイリアスは複数の環境(本番環境、ステージングなど)がある場合に役立ちます。ただし、この Codelab では default のエイリアスのみを使用します。

  1. コマンドラインの残りの手順に沿って操作します。

5. AngularFire をインストールする

プロジェクトを実行する前に、Angular CLI と AngularFire が設定されていることを確認してください。

  1. コンソールで、次のコマンドを実行します。
npm install -g @angular/cli
  1. 次に、コンソールで angularfire-start ディレクトリから次の Angular CLI コマンドを実行します。
ng add @angular/fire

これにより、プロジェクトに必要な依存関係がすべてインストールされます。

  1. プロンプトが表示されたら、Firebase コンソールでセットアップした機能(ng deploy -- hostingAuthenticationFirestoreCloud Functions (callable)Cloud MessagingCloud Storage)を選択し、コンソールのプロンプトに沿って操作します。

6. スターター アプリをローカルで実行する

これで、プロジェクトのインポートと構成が完了し、ウェブアプリを初めて実行する準備が整いました。

  1. コンソールで angularfire-start ディレクトリから次の Firebase CLI コマンドを実行します。
firebase emulators:start
  1. コマンドラインに次のレスポンスが表示されます。
✔  hosting: Local server: http://localhost:5000

Firebase Hosting エミュレータを使用してアプリをローカルで提供します。これで、ウェブアプリが http://localhost:5000 から使用できるようになります。src サブディレクトリにあるすべてのファイルが提供されます。

  1. ブラウザを使用して、http://localhost:5000 でアプリを開きます。

FriendlyChat アプリの UI が表示されますが、まだ機能していません。

angularfire-2.png

現時点では何もできませんが、協力すればすぐに利用できるようになります。ここまでは UI のレイアウトのみを行いました。

それでは、リアルタイム チャットを作成してみましょう。

7. Firebase をインポートして構成する

Firebase を構成する

使用している Firebase プロジェクトを示すように Firebase SDK を構成する必要があります。

  1. Firebase コンソールの [プロジェクトの設定] に移動します。
  2. [アプリ] カードで、構成オブジェクトが必要なアプリのニックネームを選択します。
  3. Firebase SDK スニペット ペインで [Config] を選択します。

環境ファイル /angularfire-start/src/environments/environment.ts が生成されていることがわかります。

  1. config オブジェクト スニペットをコピーして、angularfire-start/src/firebase-config.js に追加します。

environment.ts

export const environment = {
  firebase: {
    apiKey: "API_KEY",
    authDomain: "PROJECT_ID.firebaseapp.com",
    databaseURL: "https://PROJECT_ID.firebaseio.com",
    projectId: "PROJECT_ID",
    storageBucket: "PROJECT_ID.appspot.com",
    messagingSenderId: "SENDER_ID",
    appId: "APP_ID",
    measurementId: "G-MEASUREMENT_ID",
  },
};

AngularFire をインポートする

コンソールで選択した機能が /angularfire-start/src/app/app.module.ts ファイルで自動的にルーティングされていることがわかります。これにより、アプリで Firebase の機能を使用できるようになります。ただし、ローカル環境で開発するには、Emulator Suite を使用するように接続する必要があります。

  1. /angularfire-start/src/app/app.module.tsimports セクションを見つけ、非本番環境で Emulator Suite に接続するように提供されている関数を変更します。
// ...

import { provideAuth,getAuth, connectAuthEmulator } from '@angular/fire/auth';
import { provideFirestore,getFirestore, connectFirestoreEmulator } from '@angular/fire/firestore';
import { provideFunctions,getFunctions, connectFunctionsEmulator } from '@angular/fire/functions';
import { provideMessaging,getMessaging } from '@angular/fire/messaging';
import { provideStorage,getStorage, connectStorageEmulator } from '@angular/fire/storage';

// ...

provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAuth(() => {
    const auth = getAuth();
    if (location.hostname === 'localhost') {
        connectAuthEmulator(auth, 'http://127.0.0.1:9099', { disableWarnings: true });
    }
    return auth;
}),
provideFirestore(() => {
    const firestore = getFirestore();
    if (location.hostname === 'localhost') {
        connectFirestoreEmulator(firestore, '127.0.0.1', 8080);
    }
    return firestore;
}),
provideFunctions(() => {
    const functions = getFunctions();
    if (location.hostname === 'localhost') {
        connectFunctionsEmulator(functions, '127.0.0.1', 5001);
    }
    return functions;
}),
provideStorage(() => {
    const storage = getStorage();
    if (location.hostname === 'localhost') {
        connectStorageEmulator(storage, '127.0.0.1', 5001);
    }
    return storage;
}),
provideMessaging(() => {
    return getMessaging();
}),

// ...

app.module.ts

この Codelab では、Firebase Authentication、Cloud Firestore、Cloud Storage、Cloud Messaging、Performance Monitoring を使用して、それぞれのライブラリをインポートします。今後作成するアプリでは、Firebase の必要な部分のみをインポートして、アプリの読み込み時間を短くしてください。

8. ユーザー ログインを設定する

これで AngularFire が app.module.ts でインポートおよび初期化されたため、使用できるようになりました。ここでは、Firebase Authentication を使用してユーザー ログインを実装します。

Google ログインでユーザーを認証する

アプリでユーザーが [Google でログイン] ボタンをクリックすると、login 関数がトリガーされます。(設定はすでに完了しています)。この Codelab では、Firebase が Google を ID プロバイダとして使用することを承認します。ポップアップを使用しますが、Firebase では他にもいくつかの方法を使用できます。

  1. angularfire-start ディレクトリの /src/app/services/ サブディレクトリで chat.service.ts を開きます。
  2. login 関数を見つけます。
  3. 関数全体を次のコードに置き換えます。

chat.service.ts

// Signs-in Friendly Chat.
login() {
    signInWithPopup(this.auth, this.provider).then((result) => {
        const credential = GoogleAuthProvider.credentialFromResult(result);
        this.router.navigate(['/', 'chat']);
        return credential;
    })
}

ユーザーが [Log out] ボタンをクリックすると、logout 関数がトリガーされます。

  1. src/app/services/chat.service.ts ファイルに戻ります。
  2. logout 関数を見つけます。
  3. 関数全体を次のコードに置き換えます。

chat.service.ts

// Logout of Friendly Chat.
logout() {
    signOut(this.auth).then(() => {
        this.router.navigate(['/', 'login'])
        console.log('signed out');
    }).catch((error) => {
        console.log('sign out error: ' + error);
    })
}

認証状態を追跡する

それに応じて UI を更新するには、ユーザーがログインしているかログアウトしているかを確認する方法が必要です。Firebase Authentication を使用すると、認証状態が変わるたびにトリガーされる、ユーザーの状態に関する監視可能なものを取得できます。

  1. src/app/services/chat.service.ts ファイルに戻ります。
  2. 変数割り当て user$ を見つけます。
  3. 割り当て全体を次のコードに置き換えます。

chat.service.ts

// Observable user
user$ = user(this.auth);

上記のコードは、監視可能なユーザーを返す AngularFire 関数 user を呼び出します。認証状態が変わるたびに(ユーザーがログインまたはログアウトしたとき)にトリガーされます。この時点で、リダイレクト、ヘッダー ナビゲーションへのユーザー表示などを行うように UI を更新します。これらの UI 部分はすべて実装済みです。

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

  1. アプリが引き続き配信されている場合は、ブラウザでアプリを更新します。それ以外の場合は、コマンドラインで firebase emulators:start を実行して http://localhost:5000 からアプリの提供を開始してから、ブラウザで開きます。
  2. ログインボタンと Google アカウントを使用してアプリにログインします。「auth/operation-not-allowed」というエラー メッセージが表示された場合は、Firebase コンソールで Google ログインを認証プロバイダとして有効にしていることを確認します。
  3. ログインすると、プロフィール写真とユーザー名が表示されます(angularfire-3.png)。

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

このセクションでは、アプリの UI にデータを入力できるように、Cloud Firestore にデータを書き込みます。これは Firebase コンソールを使用して手動で行うことができますが、アプリ自体を使用して、基本的な Cloud Firestore の書き込みのデモを行います。

データモデル

Cloud Firestore のデータは、コレクション、ドキュメント、フィールド、サブコレクションに分割されます。チャットの各メッセージを、messages というトップレベルのコレクションにドキュメントとして保存します。

688d7bc5fb662b57.png

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

ユーザーが作成したチャット メッセージを保存するには、Cloud Firestore を使用します。

このセクションでは、ユーザーがデータベースに新しいメッセージを書き込む機能を追加します。ユーザーが [送信] ボタンをクリックすると、以下のコード スニペットがトリガーされます。メッセージ フィールドの内容を含むメッセージ オブジェクトが、messages コレクション内の Cloud Firestore インスタンスに追加されます。add() メソッドは、自動生成された ID を持つ新しいドキュメントをコレクションに追加します。

  1. src/app/services/chat.service.ts ファイルに戻ります。
  2. addMessage 関数を見つけます。
  3. 関数全体を次のコードに置き換えます。

chat.service.ts

// Adds a text or image message to Cloud Firestore.
addMessage = async(textMessage: string | null, imageUrl: string | null): Promise<void | DocumentReference<DocumentData>> => {
    let data: any;
    try {
      this.user$.subscribe(async (user) => 
      { 
        if(textMessage && textMessage.length > 0) {
          data =  await addDoc(collection(this.firestore, 'messages'), {
            name: user?.displayName,
            text: textMessage,
            profilePicUrl: user?.photoURL,
            timestamp: serverTimestamp(),
            uid: user?.uid
          })}
          else if (imageUrl && imageUrl.length > 0) {
            data =  await addDoc(collection(this.firestore, 'messages'), {
              name: user?.displayName,
              imageUrl: imageUrl,
              profilePicUrl: user?.photoURL,
              timestamp: serverTimestamp(),
              uid: user?.uid
            });
          }
          return data;
        }
      );
    }
    catch(error) {
      console.error('Error writing new message to Firebase Database', error);
      return;
    }
}

メッセージの送信をテストする

  1. アプリが引き続き配信されている場合は、ブラウザでアプリを更新します。それ以外の場合は、コマンドラインで firebase emulators:start を実行して http://localhost:5000 からアプリの提供を開始してから、ブラウザで開きます。
  2. ログインしたら、「こんにちは」などのメッセージを入力し、[送信] をクリックします。これにより、メッセージが Cloud Firestore に書き込まれます。ただし、データの取得を実装する必要があるため、実際のウェブアプリではまだデータを確認できません(Codelab の次のセクション)。
  3. Firebase コンソールで新しく追加されたメッセージを確認できます。Emulator Suite UI を開きます。[Build] セクションで [Firestore Database](またはこちらをクリックすると、新しく追加したメッセージを含む messages コレクションが表示されます)をクリックします。

6812efe7da395692.png

10. メッセージを読む

メッセージを同期する

アプリでメッセージを読み取るには、データが変更されたときにトリガーされるオブザーバブルを追加してから、新しいメッセージを表示する UI 要素を作成する必要があります。

アプリから新しく追加されたメッセージをリッスンするコードを追加します。このコードでは、messages コレクションのスナップショットを取得します。読み込み時に非常に長い履歴が表示されないようにするため、チャットの直近 12 件のメッセージのみが表示されます。

  1. src/app/services/chat.service.ts ファイルに戻ります。
  2. loadMessages 関数を見つけます。
  3. 関数全体を次のコードに置き換えます。

chat.service.ts

// Loads chat message history and listens for upcoming ones.
loadMessages = () => {
  // Create the query to load the last 12 messages and listen for new ones.
  const recentMessagesQuery = query(collection(this.firestore, 'messages'), orderBy('timestamp', 'desc'), limit(12));
  // Start listening to the query.
  return collectionData(recentMessagesQuery);
}

データベース内のメッセージをリッスンするには、collection 関数を使用してコレクションに対するクエリを作成し、リッスンするデータが含まれるコレクションを指定します。上記のコードでは、チャット メッセージが保存されている messages コレクション内の変更をリッスンしています。また、limit(12) を使用して直近の 12 件のメッセージのみをリッスンし、orderBy('timestamp', 'desc') を使用してメッセージを日付順に並べ替え、最新の 12 件のメッセージを取得するという制限も適用します。

collectionData 関数は、内部でスナップショットを使用します。クエリに一致するドキュメントに変更があると、コールバック関数がトリガーされます。これは、メッセージが削除、変更、または追加された場合などです。詳細については、Cloud Firestore のドキュメントをご覧ください。

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

  1. アプリが引き続き配信されている場合は、ブラウザでアプリを更新します。それ以外の場合は、コマンドラインで firebase emulators:start を実行して http://localhost:5000 からアプリの提供を開始してから、ブラウザで開きます。
  2. 先ほどデータベースで作成したメッセージが FriendlyChat UI(以下を参照)に表示されます。新しいメッセージを自由に書き込んでください。すぐに表示されるはずです。
  3. (省略可)Emulator スイートの [Firestore] セクションで、メッセージを手動で削除、変更、追加してみてください。変更内容は UI に反映されます。

これで完了です。アプリで Cloud Firestore ドキュメントを読み取っています。

angularfire-2.png

11. 画像を送信する

次に、画像を共有する機能を追加します。

Cloud Firestore は構造化データを保存するのに適していますが、Cloud Storage はファイルを保存するのに適しています。Cloud Storage for Firebase はファイル/blob ストレージ サービスであり、ユーザーがアプリを使用して共有する画像を保存するために使用します。

画像を Cloud Storage に保存する

この Codelab には、ファイル選択ツール ダイアログをトリガーするボタンがすでに追加されています。ファイルを選択すると、saveImageMessage 関数が呼び出され、選択したファイルへの参照を取得できます。saveImageMessage 関数は次の処理を行います。

  1. チャット フィードに「プレースホルダ」のチャット メッセージが作成され、画像のアップロード中にユーザーに「読み込み中」のアニメーションが表示されます。
  2. 画像ファイルを Cloud Storage のパス /<uid>/<file_name> にアップロードします。
  3. 画像ファイルの公開 URL を生成します。
  4. 一時的に読み込む画像の代わりに、新しくアップロードされた画像ファイルの URL でチャット メッセージを更新します。

次に、画像を送信する機能を追加します。

  1. src/chat.service.ts ファイルに戻ります。
  2. saveImageMessage 関数を見つけます。
  3. 関数全体を次のコードに置き換えます。

chat.service.ts

// Saves a new message containing an image in Firebase.
// This first saves the image in Firebase storage.
saveImageMessage = async(file: any) => {
  try {
    // 1 - You add a message with a loading icon that will get updated with the shared image.
    const messageRef = await this.addMessage(null, this.LOADING_IMAGE_URL);

    // 2 - Upload the image to Cloud Storage.
    const filePath = `${this.auth.currentUser?.uid}/${file.name}`;
    const newImageRef = ref(this.storage, filePath);
    const fileSnapshot = await uploadBytesResumable(newImageRef, file);
    
    // 3 - Generate a public URL for the file.
    const publicImageUrl = await getDownloadURL(newImageRef);

    // 4 - Update the chat message placeholder with the image's URL.
    messageRef ?
    await updateDoc(messageRef,{
      imageUrl: publicImageUrl,
      storageUri: fileSnapshot.metadata.fullPath
    }): null;
  } catch (error) {
    console.error('There was an error uploading a file to Cloud Storage:', error);
  }
}

画像の送信をテストする

  1. アプリが引き続き配信されている場合は、ブラウザでアプリを更新します。それ以外の場合は、コマンドラインで firebase emulators:start を実行して http://localhost:5000 からアプリの提供を開始してから、ブラウザで開きます。
  2. ログインしたら、左下の画像アップロード ボタン angularfire-4.png をクリックし、ファイル選択ツールを使用して画像ファイルを選択します。画像をお探しの場合は、こちらのコーヒーカップの素敵な写真をご利用ください。
  3. 選択した画像とともに、アプリの UI に新しいメッセージ angularfire-2.png が表示されます。

ログインしていない状態で画像を追加しようとすると、画像を追加するにはログインする必要があることを示すエラーが表示されます。

12. 通知を表示する

次に、ブラウザの通知のサポートを追加します。チャットに新しいメッセージが投稿されると、アプリからユーザーに通知が届きます。Firebase Cloud Messaging(FCM)は、メッセージや通知を無料で確実に配信するためのクロス プラットフォーム メッセージング ソリューションです。

FCM Service Worker を追加する

ウェブアプリには、ウェブ通知を受信して表示する Service Worker が必要です。

AngularFire が追加されたときに、メッセージング プロバイダがすでに設定されているはずです。/angularfire-start/src/app/app.module.ts のインポート セクションに次のコードが存在することを確認してください。

provideMessaging(() => {
    return getMessaging();
}),

app/app.module.ts

Service Worker は Firebase Cloud Messaging SDK を読み込んで初期化するだけで済みます。SDK が通知の表示を処理します。

FCM デバイス トークンを取得する

デバイスまたはブラウザで通知が有効になっている場合、デバイス トークンが付与されます。このデバイス トークンは、通知を特定のデバイスまたは特定のブラウザに送信するために使用します。

ユーザーがログインしたら、saveMessagingDeviceToken 関数を呼び出します。そこで、ブラウザから FCM デバイス トークンを取得して Cloud Firestore に保存します。

chat.service.ts

  1. saveMessagingDeviceToken 関数を見つけます。
  2. 関数全体を次のコードに置き換えます。

chat.service.ts

// Saves the messaging device token to Cloud Firestore.
saveMessagingDeviceToken= async () => {
    try {
      const currentToken = await getToken(this.messaging);
      if (currentToken) {
        console.log('Got FCM device token:', currentToken);
        // Saving the Device Token to Cloud Firestore.
        const tokenRef = doc(this.firestore, 'fcmTokens', currentToken);
        await setDoc(tokenRef, { uid: this.auth.currentUser?.uid });
 
        // This will fire when a message is received while the app is in the foreground.
        // When the app is in the background, firebase-messaging-sw.js will receive the message instead.
        onMessage(this.messaging, (message) => {
          console.log(
            'New foreground notification from Firebase Messaging!',
            message.notification
          );
        });
      } else {
        // Need to request permissions to show notifications.
        this.requestNotificationsPermissions();
      }
    } catch(error) {
      console.error('Unable to get messaging token.', error);
    };
}

ただし、このコードは最初は動作しません。アプリがデバイス トークンを取得できるようにするには、ユーザーがアプリに通知を表示する権限を付与する必要があります(この Codelab の次のステップ)。

通知を表示する権限をリクエストする

ユーザーがアプリに通知を表示する権限をまだ付与していない場合、デバイス トークンは提供されません。この場合、requestPermission() メソッドを呼び出します。これにより、サポートされているブラウザで、この権限を要求するブラウザ ダイアログが表示されます。

8b9d0c66dc36153d.png

  1. src/app/services/chat.service.ts ファイルに戻ります。
  2. requestNotificationsPermissions 関数を見つけます。
  3. 関数全体を次のコードに置き換えます。

chat.service.ts

// Requests permissions to show notifications.
requestNotificationsPermissions = async () => {
    console.log('Requesting notifications permission...');
    const permission = await Notification.requestPermission();
    
    if (permission === 'granted') {
      console.log('Notification permission granted.');
      // Notification permission granted.
      await this.saveMessagingDeviceToken();
    } else {
      console.log('Unable to get permission to notify.');
    }
}

デバイス トークンを取得する

  1. アプリが引き続き配信されている場合は、ブラウザでアプリを更新します。それ以外の場合は、コマンドラインで firebase emulators:start を実行して http://localhost:5000 からアプリの提供を開始してから、ブラウザで開きます。
  2. ログインすると、通知権限ダイアログ bd3454e6dbfb6723.png が表示されます。
  3. [許可] をクリックします。
  4. ブラウザの JavaScript コンソールを開きます。「Got FCM device token: cWL6w:APA91bHP...4jDPL_A-wPP06GJp1OuekTaTZI5K2Tu」というメッセージが表示されます。
  5. デバイス トークンをコピーします。これは、Codelab の次のステージで必要になります。

デバイスに通知を送信する

デバイス トークンを取得したら、通知を送信できます。

  1. Firebase コンソールの [Cloud Messaging] タブを開きます。
  2. [新しい通知] をクリックします
  3. 通知のタイトルと通知テキストを入力します。
  4. 画面の右側にある [テスト メッセージを送信] をクリックします。
  5. ブラウザの JavaScript コンソールからコピーしたデバイス トークンを入力し、プラス記号(+)をクリックします。
  6. [テスト] をクリックします

アプリがフォアグラウンドにある場合、JavaScript コンソールに通知が表示されます。

アプリがバックグラウンドで動作している場合は、次の例のようにブラウザに通知が表示されます。

de79e8638a45864c.png

13. Cloud Firestore セキュリティ ルール

データベースのセキュリティ ルールを表示する

Cloud Firestore は特定のルール言語を使用して、アクセス権、セキュリティ、データの検証を定義します。

この Codelab の冒頭で Firebase プロジェクトを設定するときに、データストアへのアクセスを制限しないように、デフォルトのセキュリティ ルールとして「テストモード」を選択しました。Firebase コンソールの [データベース] セクションの [ルール] タブで、これらのルールを表示、変更できます。

今度は、データストアへのアクセスを制限しないデフォルト ルールが表示されます。つまり、どのユーザーもデータストア内のあらゆるコレクションに対して読み取りと書き込みを行うことができます。

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write;
    }
  }
}

以下のルールを使用して制限対象のルールを更新します。

firestore.rules

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    // Messages:
    //   - Anyone can read.
    //   - Authenticated users can add and edit messages.
    //   - Validation: Check name is same as auth token and text length below 300 char or that imageUrl is a URL.
    //   - Deletes are not allowed.
    match /messages/{messageId} {
      allow read;
      allow create, update: if request.auth != null
                    && request.resource.data.name == request.auth.token.name
                    && (request.resource.data.text is string
                      && request.resource.data.text.size() <= 300
                      || request.resource.data.imageUrl is string
                      && request.resource.data.imageUrl.matches('https?://.*'));
      allow delete: if false;
    }
    // FCM Tokens:
    //   - Anyone can write their token.
    //   - Reading list of tokens is not allowed.
    match /fcmTokens/{token} {
      allow read: if false;
      allow write;
    }
  }
}

セキュリティ ルールはエミュレータ スイートで自動的に更新されます。

Cloud Storage セキュリティ ルールを表示する

Cloud Storage for Firebase は、特定のルール言語を使用してアクセス権、セキュリティ、データ検証を定義します。

この Codelab の冒頭で Firebase プロジェクトを設定したときに、認証されたユーザーにのみ Cloud Storage の使用を許可するデフォルトの Cloud Storage セキュリティ ルールを使用することを選択しました。Firebase コンソールの [Storage] セクションの [ルール] タブで、ルールを表示、変更できます。ログインしているすべてのユーザーにストレージ バケット内のファイルの読み取りと書き込みを許可するデフォルトのルールが表示されます。

rules_version = '2';

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

ルールを次のように更新します。

  • 各ユーザーに自身の特定のフォルダのみへの書き込みを許可する
  • すべてのユーザーに Cloud Storage からの読み取りを許可する
  • アップロードするファイルが画像であることを確認してください
  • アップロードできる画像のサイズを最大 5 MB に制限する

これは、次のルールを使用して実装できます。

storage.rules

rules_version = '2';

// Returns true if the uploaded file is an image and its size is below the given number of MB.
function isImageBelowMaxSize(maxSizeMB) {
  return request.resource.size < maxSizeMB * 1024 * 1024
      && request.resource.contentType.matches('image/.*');
}

service firebase.storage {
  match /b/{bucket}/o {
    match /{userId}/{messageId}/{fileName} {
      allow write: if request.auth != null && request.auth.uid == userId && isImageBelowMaxSize(5);
      allow read;
    }
  }
}

14. Firebase Hosting を使用してアプリをデプロイする

Firebase には、アセットとウェブアプリを提供するためのホスティング サービスが用意されています。Firebase CLI を使用して、ファイルを Firebase Hosting にデプロイできます。デプロイする前に、デプロイするローカル ファイルを firebase.json ファイルで指定する必要があります。このステップは、この Codelab でファイルを提供するために必要だったため、この Codelab ではすでに実施しています。ホスティング設定は hosting 属性で指定されます。

firebase.json

{
  // If you went through the "Cloud Firestore Security Rules" step.
  "firestore": {
    "rules": "firestore.rules"
  },
  // If you went through the "Storage Security Rules" step.
  "storage": {
    "rules": "storage.rules"
  },
  "hosting": {
    "public": "./public"
  }
}

これらの設定により、./public ディレクトリ("public": "./public")内のすべてのファイルをデプロイするよう CLI に指示します。

  1. コマンドラインでアプリのローカル angularfire-start ディレクトリにアクセスしていることを確認します。
  2. 次のコマンドを実行して、ファイルを Firebase プロジェクトにデプロイします。
ng deploy

次に、[Firebase] オプションを選択し、コマンドラインのプロンプトに従います。

  1. コンソールに次のように表示されます。
=== Deploying to 'friendlychat-1234'...

i  deploying firestore, storage, hosting
i  storage: checking storage.rules for compilation errors...
✔  storage: rules file storage.rules compiled successfully
i  firestore: checking firestore.rules for compilation errors...
✔  firestore: rules file firestore.rules compiled successfully
i  storage: uploading rules storage.rules...
i  firestore: uploading rules firestore.rules...
i  hosting[friendlychat-1234]: beginning deploy...
i  hosting[friendlychat-1234]: found 8 files in ./public
✔  hosting[friendlychat-1234]: file upload complete
✔  storage: released rules storage.rules to firebase.storage/friendlychat-1234.appspot.com
✔  firestore: released rules firestore.rules to cloud.firestore
i  hosting[friendlychat-1234]: finalizing version...
✔  hosting[friendlychat-1234]: version finalized
i  hosting[friendlychat-1234]: releasing new version...
✔  hosting[friendlychat-1234]: release complete

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/friendlychat-1234/overview
Hosting URL: https://friendlychat-1234.firebaseapp.com
  1. 2 つの Firebase サブドメインで Firebase Hosting を使用して、グローバル CDN で完全にホストされたウェブアプリにアクセスします。
  • https://<firebase-projectId>.firebaseapp.com
  • https://<firebase-projectId>.web.app

または、コマンドラインで firebase open hosting:site を実行することもできます。

Firebase Hosting の仕組みの詳細については、ドキュメントをご覧ください。

プロジェクトの Firebase コンソールの [Hosting] セクションに移動すると、デプロイの履歴、アプリの以前のバージョンにロールバックする機能、カスタム ドメインを設定するためのワークフローなど、有用なホスティング情報とツールが表示されます。

15. お疲れさまでした

Firebase を使用してリアルタイム チャット ウェブ アプリケーションを構築することができました。

学習した内容

  • Firebase Authentication
  • Cloud Firestore
  • Firebase SDK for Cloud Storage
  • Firebase Cloud Messaging
  • Firebase Performance Monitoring
  • Firebase Hosting

次のステップ

詳細

16. (省略可)App Check で適用する

Firebase App Check は、不要なトラフィックからサービスを保護し、バックエンドを不正行為から保護するのに役立ちます。このステップでは、認証情報の検証を追加し、App Check と reCAPTCHA Enterprise を使用して未承認のクライアントをブロックします。

まず、App Check と reCAPTCHA を有効にする必要があります。

reCAPTCHA Enterprise の有効化

  1. Cloud コンソールの [セキュリティ] で、[reCaptcha Enterprise] を見つけて選択します。
  2. プロンプトが表示されたらサービスを有効にし、[キーを作成] をクリックします。
  3. 指示に従って表示名を入力し、プラットフォーム タイプとして [ウェブサイト] を選択します。
  4. デプロイした URL を [ドメインリスト] に追加し、[チェックボックスによる本人確認を使用する] オプションがオフになっていることを確認します。
  5. [鍵を作成] をクリックし、生成された鍵を安全な場所に保存します。これはこのステップの後半で必要になります。

App Check を有効にする

  1. Firebase コンソールで、左側のパネルにある [ビルド] セクションを見つけます。
  2. [App Check] をクリックし、[ログイン方法] タブをクリックして [App Check] に移動します。
  3. [登録] をクリックし、画面の指示に従って reCaptcha Enterprise キーを入力し、[保存] をクリックします。
  4. API ビューで [ストレージ] を選択し、[適用] をクリックします。Cloud Firestore についても同じことを行います。

これで、App Check が適用されるようになりました。アプリを更新し、チャット メッセージを表示または送信してみます。次のエラー メッセージが表示されます。

Uncaught Error in snapshot listener: FirebaseError: [code=permission-denied]: Missing or insufficient permissions.

App Check はデフォルトで、未検証のリクエストをブロックします。次に、アプリに検証を追加します。

environment.ts ファイルに移動し、reCAPTCHAEnterpriseKeyenvironment オブジェクトに追加します。

export const environment = {
  firebase: {
    apiKey: 'API_KEY',
    authDomain: 'PROJECT_ID.firebaseapp.com',
    databaseURL: 'https://PROJECT_ID.firebaseio.com',
    projectId: 'PROJECT_ID',
    storageBucket: 'PROJECT_ID.appspot.com',
    messagingSenderId: 'SENDER_ID',
    appId: 'APP_ID',
    measurementId: 'G-MEASUREMENT_ID',
  },
  reCAPTCHAEnterpriseKey: {
    key: "Replace with your recaptcha enterprise site key"
  },
};

key の値は、reCaptcha Enterprise トークンに置き換えます。

次に、app.module.ts ファイルに移動し、次のインポートを追加します。

import { getApp } from '@angular/fire/app';
import {
  ReCaptchaEnterpriseProvider,
  initializeAppCheck,
  provideAppCheck,
} from '@angular/fire/app-check';

同じ app.module.ts ファイルに、次のグローバル変数宣言を追加します。

declare global {
  var FIREBASE_APPCHECK_DEBUG_TOKEN: boolean;
}

@NgModule({ ...

インポートで、ReCaptchaEnterpriseProvider を使用して App Check の初期化を追加し、isTokenAutoRefreshEnabledtrue に設定してトークンの自動更新を許可します。

imports: [
BrowserModule,
AppRoutingModule,
CommonModule,
FormsModule,
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAppCheck(() => {
const appCheck = initializeAppCheck(getApp(), {
  provider: new ReCaptchaEnterpriseProvider(
  environment.reCAPTCHAEnterpriseKey.key
  ),
  isTokenAutoRefreshEnabled: true,
  });
  if (location.hostname === 'localhost') {
    self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
  }
  return appCheck;
}),

ローカルテストを許可するには、self.FIREBASE_APPCHECK_DEBUG_TOKENtrue に設定します。localhost でアプリを更新すると、コンソールに次のようなデバッグ トークンがログに記録されます。

App Check debug token: CEFC0C76-7891-494B-B764-349BDFD00D00. You will need to add it to your app's App Check settings in the Firebase console for it to work.

Firebase コンソールで App Check の [アプリビュー] に移動します。

オーバーフロー メニューをクリックして、[デバッグ トークンを管理] を選択します。

次に、[デバッグ トークンを追加] をクリックし、プロンプトに従ってコンソールからデバッグ トークンを貼り付けます。

chat.service.ts ファイルに移動し、次のインポートを追加します。

import { AppCheck } from '@angular/fire/app-check';

同じ chat.service.ts ファイルで、他の Firebase サービスとともに App Check を挿入します。

export class ChatService {
appCheck: AppCheck = inject(AppCheck);
...

これで完了です。これで、アプリで App Check が機能するようになりました。