LoginSignup
8

More than 3 years have passed since last update.

FriendlyChat-Webを動かしながら覚えるFirebase

Last updated at Posted at 2019-06-21

Firebaseを知る

とある新しくアサインされた案件で、Firebase触ることになりましたので、調べたことを記述します。

FirebaseはBaasという括り

 クラウドサービスではBaaSの括りに入る。クラウドサービスには3種類ほど存在するので、それぞれの違いを明確にします。本当はこれに加えてSaaSもありますが、これはもうソフトウェアなので、言ってしまえば利用するサービスそのもの(例えばGmail とか)までが決まっているので、省きます。今回、以下の3つにフォーカスしたのは、バックエンドをサポートしてくれる範囲を明示したかった意図があります。

IaaS

Infrastructure as a Servicの略。システム稼働に必要な仮想サーバ、ハードディスク、ファイヤーウォールなどのインフラのみを提供するサービス。AWSのEC2などが代表格。クラウドサービスの中では最も自由度が高く、OSやハードのスペックを任意に決めることができる。もちろん、使用言語や、フレームワーク、ミドルウェアも開発側で決めることができる分、そのそれを使う知識、時間コストがあります。

PaaS

Platform as a Serviceの略である。プラットフォームからぽちぽちするだけで、設置が簡単にできます。ハードからOS、ウェア、フレームワークまでが提供済みであり、IaaSよりは自由度が減ります。この上で開発された独自サービスの規模、ユーザーのアクセス頻度などに応じて、動的にサーバーのCPUやメモリ、ネットワークの帯域幅などのリソースを割り当てることができる機能も備えています。AWSのマネジメントコンソールや、GCP、Azureなどが代表格。

BaaS

Backend as a Serviceの略です。サーバーサイドにおけるバックエンドシステムを全て担います。バックエンドシステムは自動生成される。これらのバックエンドはAPIや、組み込みのライブラリの形で提供されており。開発するシステムの中に埋め込む形でバックエンドを機能させて、開発を進めることができる。代表格が今回のFirebaseです。

Firebaseをいじる

簡単に、Webチャットアプリをデプロイしてみます。チュートリアル用のアプリのリポジトリがGithubで公開されているので、まずはこれをgit cloneしてきます。

公式リファレンスはこちらです。
リファレンス;Firestore

公開されているリポジトリ
firebase/friendlychat-web

$ git colne git@github.com:firebase/friendlychat-web.git

Firebase consoleへ

Cloud FirestoreでDBを作成

Cloud Firestore は、Firebase のモバイルアプリ開発用の新しい主力データベースです。直感的な新しいデータモデルで、Realtime Databaseをさらに効果的にしています。Cloud Firestore は、Realtime Database よりも多彩で高速なクエリと高性能なスケーリングが特長です。

もう1つRealtime Databaseなるものもあります。どちらもNo SQLです。

コンソール上から、Firestoreから作成を選択し、テストモードで作成してください。

プロジェクト作成とロケーション確定

 まずは、Firebaseのコンソールから、新しくプロジェクトを立ち上げます。プロジェクト名を入力して、Firestoreのロケーションを設定します。リファレンスを確認したところ、asia-northeast1が日本ということで、こちらを選択します。

スクリーンショット 2019-06-18 11.02.22.png

アプリ種類選定とスクリプト埋め込み

 その次に、iOS・Andoroid・webの3つのどれかを選択します。今回は、webを選択します。アプリのニックネームとFirebase Hostingをセットする状態で始めます。

Firebase SDKスニペットの追加を行います。CDNのスニペットを貼り付けます。

<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/6.2.0/firebase-app.js"></script>

<!-- TODO: Add SDKs for Firebase products that you want to use
     https://firebase.google.com/docs/web/setup#config-web-app -->

<script>
  // Your web app's Firebase configuration
  var firebaseConfig = {
    apiKey: "AIzaSyCeV8Kz7JfmmA6xi3vlW1irQegzP77cSU0",
    authDomain: "friendlychat-f7746.firebaseapp.com",
    databaseURL: "https://friendlychat-f7746.firebaseio.com",
    projectId: "friendlychat-f7746",
    storageBucket: "friendlychat-f7746.appspot.com",
    messagingSenderId: "1053657019228",
    appId: "1:1053657019228:web:e826e4941f91d633"
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
</script>

このスクリプトを、下記のようにに埋め込むこみます。コメントアウトでインポートする部分が書かれているので、ここに埋め込みます。コメントアウトは邪魔っけだったので、スクリプトタグのみを追加しました。

/friendlychat-web/web-start/public/index.html
<script src="/__/firebase/6.1.1/firebase-app.js"></script>
<script src="/__/firebase/6.1.1/firebase-auth.js"></script>
<script src="/__/firebase/6.1.1/firebase-storage.js"></script>
<script src="/__/firebase/6.1.1/firebase-messaging.js"></script>
<script src="/__/firebase/6.1.1/firebase-firestore.js"></script>
<script src="/__/firebase/6.1.1/firebase-performance.js"></script>
<script src="/__/firebase/init.js"></script>

<script src="scripts/main.js"></script>

<script>
  // Your web app's Firebase configuration
  var firebaseConfig = {
    apiKey: "AIzaSyCeV8Kz7JfmmA6xi3vlW1irQegzP77cSU0",
    authDomain: "friendlychat-f7746.firebaseapp.com",
    databaseURL: "https://friendlychat-f7746.firebaseio.com",
    projectId: "friendlychat-f7746",
    storageBucket: "friendlychat-f7746.appspot.com",
    messagingSenderId: "1053657019228",
    appId: "1:1053657019228:web:e826e4941f91d633"
  };
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
</script>

埋め込みが完了したら、コンソール上から次へを選択してください。

Firebase CLIのインストール

これでローカルで開発しているものをFirebaseのホスティングサービスへアップします。

npm経由でCLIをインストールします。

$ npm install -g firebase-tools

login

firebaseのコマンドでログインを行うと、ブラウザが立ち上がるので、アカウント使用を許可します。

$ firebase login
? Allow Firebase to collect anonymous CLI usage and error reporting information?(Y or n)Y

Waiting for authentication...

✔  Success! Logged in as your email addoress

use --add

プロジェクトを開始します。アプリのルートディレクトリ内に入ってから、initしてください。

$ cd friendlychat-web/web-start
$ firebase use --add
? Which project do you want to add? friendlychat-f7746
? What alias do you want to use for this project? (e.g. staging) test

Created alias test for friendlychat-f7746.
Now using alias test (friendlychat-f7746)

serve --only hosting

$ firebase serve --only hosting
i  hosting: Serving hosting files from: ./public
✔  hosting: Local server: http://localhost:5000

にこれでアクセスしてみてください。アプリの画面が立ち上がると思います。

セットアップ

サインイン
/friendlychat-web/web-start/script/main.js
function signIn() {
  var provider = new firebase.auth.GoogleAuthProvider();
  firebase.auth().signInnwithPopup(provider);
}
サインアウト
/friendlychat-web/web-start/script/main.js
function signOut() {
  firebase.auth().signOut();
}
Firebase初期化
/friendlychat-web/web-start/script/main.js
function initFirebaseAuth() {
  firebase.auth().onAuthStateChanged(authStateObserver);
}
ユーザーネームとトップ画像を返す
/friendlychat-web/web-start/script/main.js
// Returns the signed-in user's profile Pic URL.
function getProfilePicUrl() {
  return firebase.auth().currentUser.photoURL || '/images/profile_placeholder.png';
}

// Returns the signed-in user's display name.
function getUserName() {
  return firebase.auth().currentUser.displayName;
}
サインイン時のプロフィールを返す
/friendlychat-web/web-start/script/main.js
function isUserSignedIn() {
  return !!firebase.auth().currentUser;
}
google Authenticationを使用する

Firebase consoleから Authenticationをサイドバーから選択し、Authenticationのログイン方法から、Googleを選択し、有効化してください。

ここでもしかしたらエラーがでるかもしれません。

http://localhost:5000
アクセスして、googleアカウントで認証しようとすると、

Error 403: restricted_client This app is not yet configured to make OAuth requests. To do that, set up the app’s OAuth consent screen in the Google Cloud Console.

こんなのが出てきてしまうと思いますが、Google APIsのOAuth認証情報が欠けているので、このエラーから詳細リンクがあるので、そこから認証情報を保存をしてあげると、サーバーキーとブラウザキーと、クライアントIDを自動で生成してくれます。

これで再度、
http://localhost:5000
にアクセスすれば、ちゃんとgoogleアカウントでログインができるようになると思います。

メッセージを保存する
/friendlychat-web/web-start/script/main.js
function saveMessage(messageText) {
  return firebase.firestore().collection('messages').add({
    name: getUserName(),
    text: messageText,
    profilePicUrl: getProfilePicUrl(),
    timestamp: firebase.firestore.FieldValue.serverTimestamp()
  }).catch(function(error) {
    console.error('Error writing new message to Firebase Database', error);
  });
}

この時点で先に設定した、Cloud Firesroreに入力したメッセージが保存されます。コンソール上から、サイドバーのデータベースを選択して、入力内容が入っているか確認してみてください。

メッセージがUIで読めるようにする
/friendlychat-web/web-start/script/main.js
function loadMessages() {
  // Create the query to load the last 12 messages and listen for new ones.
  var query = firebase.firestore()
                  .collection('messages')
                  .orderBy('timestamp', 'desc')
                  .limit(12);

  // Start listening to the query.
  query.onSnapshot(function(snapshot) {
    snapshot.docChanges().forEach(function(change) {
      if (change.type === 'removed') {
        deleteMessage(change.doc.id);
      } else {
        var message = change.doc.data();
        displayMessage(change.doc.id, message.timestamp, message.name,
                       message.text, message.profilePicUrl, message.imageUrl);
      }
    });
  });
}
Cloud Storage for Firebaseで画像を保存できるようになる
/friendlychat-web/web-start/script/main.js
function saveImageMessage(file) {
  // 1 - We add a message with a loading icon that will get updated with the shared image.
  firebase.firestore().collection('messages').add({
    name: getUserName(),
    imageUrl: LOADING_IMAGE_URL,
    profilePicUrl: getProfilePicUrl(),
    timestamp: firebase.firestore.FieldValue.serverTimestamp()
  }).then(function(messageRef) {
    // 2 - Upload the image to Cloud Storage.
    var filePath = firebase.auth().currentUser.uid + '/' + messageRef.id + '/' + file.name;
    return firebase.storage().ref(filePath).put(file).then(function(fileSnapshot) {
      // 3 - Generate a public URL for the file.
      return fileSnapshot.ref.getDownloadURL().then((url) => {
        // 4 - Update the chat message placeholder with the image's URL.
        return messageRef.update({
          imageUrl: url,
          storageUri: fileSnapshot.metadata.fullPath
        });
      });
    });
  }).catch(function(error) {
    console.error('There was an error uploading a file to Cloud Storage:', error);
  });
}

これに加えて、Cloud Storage for Firebaseを使用して、画像や動画のコンテンツをここで格納します。コンソール上からサイドバーのStorageを選択し、こちらを起動してください。

これで画像を送れるようになっているはずです。試してみてください。

Firebase Cloud Messagingでプッシュ通知を送れるようにする
/friendlychat-web/web-start/script/manifest.json
{
  "name": "Friendly Chat",
  "short_name": "Friendly Chat",
  "start_url": "/index.html",
  "display": "standalone",
  "orientation": "portrait"
  "gcm_sender_id": "10536****28"  #ここが追加する部分です。

追加するものは、送信者IDなるものなのですが、見つけにくいです。コンソール上のサイドバーから、歯車のマークをクリックし、プロジェクトの設定を選択してください。そして、設定のタブの中にクラウドメッセージングというのがあるので、これを選択してください。そうすると、送信者IDを見ることができます。

今度は、新しくファイルを作り、下記のように記述をします。

/friendlychat-web/web-start/firebase-messaging-sw.js
importScripts('/__/firebase/6.0.4/firebase-app.js');
importScripts('/__/firebase/6.0.4/firebase-messaging.js');
importScripts('/__/firebase/init.js');

firebase.messaging();

続いてデバイストークンを保存する関数を作ります。

/friendlychat-web/web-start/script/main.js
function saveMessagingDeviceToken() {
  firebase.messaging().getToken().then(function(currentToken) {
    if (currentToken) {
      console.log('Got FCM device token:', currentToken);
      // Saving the Device Token to the datastore.
      firebase.firestore().collection('fcmTokens').doc(currentToken)
          .set({uid: firebase.auth().currentUser.uid});
    } else {
      // Need to request permissions to show notifications.
      requestNotificationsPermissions();
    }
  }).catch(function(error){
    console.error('Unable to get messaging token.', error);
  });
}

続いて通知の許可を与える関数を作ります。

/friendlychat-web/web-start/script/main.js
function requestNotificationsPermissions() {
  console.log('Requesting notifications permission...');
  firebase.messaging().requestPermission().then(function() {
    // Notification permission granted.
    saveMessagingDeviceToken();
  }).catch(function(error) {
    console.error('Unable to get permission to notify.', error);
  });
}

この時点で、
http://localhost:5000/

こちらにアクセスをしてみて、通知の許可をするためのポップアップが出ると思うので、許可するを選択してください。これで、通知を受け取る準備ができました。

早速、通知を飛ばしてみましょう。

$ curl -X POST -H "Authorization: key=Server_Key" -H "Content-Type: application/json" -d '{
"notification": {
 "title": "お前をみているぞ",
 "body": "お前が見えているぞ",
 "icon": "/images/icon.png",
 "click_action": "http://localhost:5000"
},
"to": "FCM_device_token"
}' "https://fcm.googleapis.com/fcm/send"

このcurlコマンドでメッセージを飛ばしてみます。この引数に必要なのが、サーバーキーと、FCMデバイストークンです。
サーバーキーは先程の送信者IDがある場所にありますので、こちらを、コピペしてください。
FCMデバイストークンは、
http://localhost:5000/
で、検証ツールを開き、consoleのタブを開いてみると、表示されているはずなので、それをコピペしてください。そうして、このコマンドを打つと、通知が表示されます。

スクリーンショット 2019-06-21 17.07.57.png

Cloud Firebase Security rulesを変更する

サイドバーのDatabaseから、ルールを選択してください。
ここにスクリプトをかけるテキストボックスがあるので、下記のスクリプトを書いてください。

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;
    }
  }
}

このスクリプトを書いたら、公開ボタンを押して、ルールを公開設定してください。
そして、このスクリプトをローカルにファイルとして、作成します。ファイル名は、firestore.rulesです。

次に、作ったファイルを読ませるので、jsonファイルに追記をします。

~/friendlychat-web/web-start/firebase.json
{
  "firestore": {
    "rules": "firestore.rules" #ここを追加します。
  },
  "hosting": {
    "public": "./public",
    "headers": [{
      "source" : "**/*.@(js|html)",
      "headers" : [ {
        "key" : "Cache-Control",
        "value" : "max-age=0"
      } ]
    }]
  }
}

これで、このセキュリティルールをアプリへデプロイします。

$ firebase deploy --only firestore
⚠  functions: package.json indicates an outdated version of firebase-functions.
 Please upgrade using npm install --save firebase-functions@latest in your functions directory.

=== Deploying to 'friendlychat-f7746'...

i  deploying firestore
i  firestore: checking firestore.rules for compilation errors...
✔  firestore: rules file firestore.rules compiled successfully
i  firestore: uploading rules firestore.rules...
✔  firestore: released rules firestore.rules to cloud.firestore

✔  Deploy complete!
Cloud Storage ruleを変更する。

今度は、コンソール上からStorageを選択し、ルールのタブを開き、

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;
    }
  }
}

上記のスクリプトを記述し、公開します。そして、ローカルにファイルを作成します。ファイル名は、storage.rulesです。

こちらも読み込ませるために、jsonに追加します。

~/friendlychat-web/web-start/firebase.json
{
  "firestore": {
    "rules": "firestore.rules"
  },
  "storage": {
    "rules": "storage.rules" #ここを追記してください
  },
  "hosting": {
    "public": "./public",
    "headers": [{
      "source" : "**/*.@(js|html)",
      "headers" : [ {
        "key" : "Cache-Control",
        "value" : "max-age=0"
      } ]
    }]
  }
}

そしてこちらも最後にデプロイします。

$ firebase deploy --only storage
⚠  functions: package.json indicates an outdated version of firebase-functions.
 Please upgrade using npm install --save firebase-functions@latest in your functions directory.

=== Deploying to 'friendlychat-f7746'...

i  deploying storage
i  storage: checking storage.rules for compilation errors...
✔  storage: rules file storage.rules compiled successfully
i  storage: uploading rules storage.rules...
✔  storage: released rules storage.rules to firebase.storage/friendlychat-f7746.appspot.com

✔  Deploy complete!
アプリのレスポンスをFirebase Performance Monitoringを使って監視する。

このようにライブラリを読み込ませて、コンソール上からパフォーマンスをみることができます。デプロイしたら、使えるようになるそうです。

~/friendlychat-web/web-start/public/index.html
<script type="text/javascript">!function(n,e){var t,o,i,c=[],f={passive:!0,capture:!0},r=new Date,a="pointerup",u="pointercancel";function p(n,c){t||(t=c,o=n,i=new Date,w(e),s())}function s(){o>=0&&o<i-r&&(c.forEach(function(n){n(o,t)}),c=[])}function l(t){if(t.cancelable){var o=(t.timeStamp>1e12?new Date:performance.now())-t.timeStamp;"pointerdown"==t.type?function(t,o){function i(){p(t,o),r()}function c(){r()}function r(){e(a,i,f),e(u,c,f)}n(a,i,f),n(u,c,f)}(o,t):p(o,t)}}function w(n){["click","mousedown","keydown","touchstart","pointerdown"].forEach(function(e){n(e,l,f)})}w(n),self.perfMetrics=self.perfMetrics||{},self.perfMetrics.onFirstInputDelay=function(n){c.push(n),s()}}(addEventListener,removeEventListener);</script>
最後に全てをデプロイします。
$ firebase deploy --except functions
⚠  functions: package.json indicates an outdated version of firebase-functions.
 Please upgrade using npm install --save firebase-functions@latest in your functions directory.

=== Deploying to 'friendlychat-f7746'...

i  deploying storage, firestore, 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-f7746]: beginning deploy...
i  hosting[friendlychat-f7746]: found 8 files in ./public
✔  hosting[friendlychat-f7746]: file upload complete
✔  storage: released rules storage.rules to firebase.storage/friendlychat-f7746.appspot.com
✔  firestore: released rules firestore.rules to cloud.firestore
i  hosting[friendlychat-f7746]: finalizing version...
✔  hosting[friendlychat-f7746]: version finalized
i  hosting[friendlychat-f7746]: releasing new version...
✔  hosting[friendlychat-f7746]: release complete

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/friendlychat-f7746/overview
Hosting URL: https://friendlychat-f7746.firebaseapp.com

そして、下記コマンドで、web上で公開できるようです。

$ firebase open hosting:site

まとめ

複数のサービスをライブラリを呼び出す形で利用できる。
それらのサービスは、jsonでまとめられている。
NoSQLですが、インデックスが使える。
モニタリングも簡単。
アプリ開発に専念できるbaasの名を冠するのは伊達じゃないということですね。
Dockerなんて・・・

こんな感じで使えるようになります。
スクリーンショット 2019-06-21 18.49.23.png

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8