みかづきブログ・カスタム

基本的にはちょちょいのほいです。

プロトタイプでCloud Firestoreをさささっと使う(モジュールバンドラー無し ver.) 🔥

本番環境でCloud Firestoreを使う場合、セキュリティルールをしっかり設定しないといけないですが、プロトタイプとして誰でも読み書きOKとしてさささっと実装したい場合もあります。

今回は、

  • ログイン不要で誰でもデータを読み書きできる
  • モジュールバンドラーなしでHTML、CSS、JavaScriptを書く

という方針で、さささっとCloud Firestoreを使ってみようと思います。

DEMO

lighting-switch-prototype.web.app

ボタンを押すと、電気をつけたり消したりすることができます。
電気のON・OFFはCloud Firestoreで管理しているので、すべてのユーザー間でリアルタイムに共有されます。
誰かが電気を消せば、開かれているブラウザ上に表示されている電気がすべて消えるということです。

実装手順

❶ パッケージを用意する

yarn add firebase firebase-tools

❷ package.jsonを編集する

package.json
{
  "name": "lighting-switch-prototype",
  "scripts": {
    "login": "firebase login",
    "init": "firebase init",
    "dev": "firebase emulators:start",
    "deploy": "firebase deploy"
  },
  "dependencies": {
    "firebase": "^10.3.1",
    "firebase-tools": "^12.5.2"
  }
}

❸ Firebaseのコンソールからプロジェクトをつくり、Cloud Firestoreを有効にする

  • ルールはテストモードで開始する
  • ロケーションは近いところを選ぶ

firebase.google.com

❹ FirebaseのコンソールからFirestoreにデータを入れる

led / isOne に、{ value: false } を入れておきます。

❺ FirebaseのコンソールからAuthenticationのログイン方法から匿名ログインを有効にする


❺ Firebaseプロジェクトを初期化する

ログイン
yarn run login
初期化
yarn run init

初期化後、❸で作成したプロジェクトに紐付けます。

必要なものを有効化
◉ Firestore: Configure security rules and indexes files for Firestore
◉ Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys
◉ Emulators: Set up local emulators for Firebase products
必要なエミュレータを有効化
◉ Authentication Emulator
◉ Firestore Emulator
◉ Hosting Emulator

いろいろ選択肢が出てきますが、基本的にEnterを連打でOKです。

❺ コードを書く

firestore.rules
rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth != null && request.auth.token.provider_id == 'anonymous';
    }
  }
}
public/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>💡</title>

    <!-- update the version number as needed -->
    <script defer src="/__/firebase/10.3.1/firebase-app-compat.js"></script>
    <!-- include only the Firebase features as you need -->
    <script defer src="/__/firebase/10.3.1/firebase-auth-compat.js"></script>
    <script defer src="/__/firebase/10.3.1/firebase-database-compat.js"></script>
    <script defer src="/__/firebase/10.3.1/firebase-firestore-compat.js"></script>
    <script defer src="/__/firebase/10.3.1/firebase-functions-compat.js"></script>
    <script defer src="/__/firebase/10.3.1/firebase-messaging-compat.js"></script>
    <script defer src="/__/firebase/10.3.1/firebase-storage-compat.js"></script>
    <script defer src="/__/firebase/10.3.1/firebase-analytics-compat.js"></script>
    <script defer src="/__/firebase/10.3.1/firebase-remote-config-compat.js"></script>
    <script defer src="/__/firebase/10.3.1/firebase-performance-compat.js"></script>
    <!-- 
      initialize the SDK after all desired features are loaded, set useEmulator to false
      to avoid connecting the SDK to running emulators.
    -->
    <script defer src="/__/firebase/init.js?useEmulator=true"></script>
    <style>
      * {
        margin: 0;
        padding: 0;
      }

      div {
        display: flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
      }

      [data-is-on] {
        font-size: 80px;
        background: radial-gradient(#FAFAFA, #E0E0E0);
      }

      [data-is-on]::before {
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        content: '';
        background: radial-gradient(#424242, #212121);
        transition: opacity .2s ease-in-out;
      }

      [data-is-on='true']::before {
        opacity: 0;
      }

      [data-is-on='true'] dt {
        opacity: 1;
      }

      [data-is-on='true'] #btn-on {
        display: none;
      }

      [data-is-on='false']::before {
        opacity: 1;
      }

      [data-is-on='false'] dt {
        opacity: .2;
      }

      [data-is-on='false'] #btn-off {
        display: none;
      }

      dl {
        position: relative;
      }

      dd {
        display: flex;
        align-items: center;
        justify-content: center;
      }

      button {
        padding: 8px;
        font-size: 16px;
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <div data-is-on="false">
      <dl>
        <dt>💡</dt>
        <dd>
          <button id="btn-on">ON</button>
          <button id="btn-off">OFF</button>
        </dd>
      </dl>
    </div>
    <script>
      document.addEventListener('DOMContentLoaded', function() {
        const loadEl = document.querySelector('#load');
        // // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
        // // The Firebase SDK is initialized and available here!
        //
        // firebase.auth().onAuthStateChanged(user => { });
        // firebase.database().ref('/path/to/ref').on('value', snapshot => { });
        // firebase.firestore().doc('/foo/bar').get().then(() => { });
        // firebase.functions().httpsCallable('yourFunction')().then(() => { });
        // firebase.messaging().requestPermission().then(() => { });
        // firebase.storage().ref('/path/to/ref').getDownloadURL().then(() => { });
        // firebase.analytics(); // call to activate
        // firebase.analytics().logEvent('tutorial_completed');
        // firebase.performance(); // call to activate
        //
        // // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥

        try {
          let app = firebase.app();
          let features = [
            'auth', 
            'database', 
            'firestore',
            'functions',
            'messaging', 
            'storage', 
            'analytics', 
            'remoteConfig',
            'performance',
          ].filter(feature => typeof app[feature] === 'function');

          firebase.auth().signInAnonymously().then(async () => {
            const db = firebase.firestore(app);
            const isOnRef = await db.doc('led/isOn');
            const isOnDoc = await isOnRef.get();
            const isOnElm = document.querySelector('[data-is-on]');
            const btnOn = document.getElementById('btn-on');
            const btnOff = document.getElementById('btn-off');

            isOnRef.onSnapshot((doc) => {
              if (doc.data()) {
                isOnElm.dataset.isOn = doc.data().value;
              }
            });

            btnOn.addEventListener('click', async () => {
              if (isOnDoc.exists) {
                await isOnRef.set({ value: true });
              }
            });

            btnOff.addEventListener('click', async () => {
              if (isOnDoc.exists) {
                await isOnRef.set({ value: false });
              }
            });
          });
        } catch (e) {
          console.error(e);
        }
      });
    </script>
  </body>
</html>

❼ エミュレータのFirestoreにも値を入れる

❹と同じ値を入れます。

❽ エミュレータで確認する

yarn dev

❾ デプロイする

yarn deploy

こんな感じです!

プロトタイプなので、心配無用なのかもしれないですが、匿名ログインは月間のアクティブユーザー数が5万ユーザーを超えると料金が発生します。

firebase.google.com