はじめに

Firebaseを使うとサーバーサイドの知識無しに簡単なアプリを作ることができます。今回はFirebaseのデモとして、Amazonのウィッシュリストの場所版、「いきたいリスト」を作成するアプリを作ってみました(公開中のものは改修中で見れない場合があります)(スマートフォンでの閲覧を推奨します)。コロナウイルスの流行が落ち着いて外出自粛が緩和された場合に備えて、いきたい場所を周りのひとにアピールしておきましょう!!
なお、記事の内容が多いため、前編と後編に分けさせていただきます。後編はこちらです。

アプリの要件

  • 「いきたい場所(必須)」、「ひとこと(任意)」、「場所に関連するURL(任意))」を登録する
  • いきたい場所の追加・削除ができる(編集は後々・・)
    • 削除は自分が追加したもののみできる
  • いきたいリストの閲覧は誰のものでもできる
  • いきたいリストを作成したい場合はユーザーの登録が必要
  • いきたい場所と場所に関連するURLの入力を自動補完したい

使うもの

  • jQuery
    • javascriptを簡単に書くため
  • Cloud Firestore
    • ユーザーが登録したデータを管理するため
  • Firebase Authentication
    • ユーザー認証をおこなうため
  • Google Places API
    • 入力の補完をおこなうため

Firebaseの準備

Firebaseのプロジェクトを追加または作成する

Firebaseのプロジェクトを追加または作成する
以下の画面からプロジェクトを追加・作成します。

今回はGoogle Places APIを使う予定があるため、先にGCPでプロジェクトを作成しておきました。FirebaseはGCPと連携しているため、GCPのプロジェクトに組み込むことができます。GCPのプロジェクトにFirebaseを組み込む場合はプロジェクト名をつける際にGCPのプロジェクトを参照できるため、そこで連携させたいプロジェクト名を選択します。

以降は手順に沿って進めれば問題ないと思います。料金プランを選ぶ際は、お試しの開発であれば無料のSparkプランがおすすめですが、GCPのプロジェクトを選択した場合は課金がFirebaseとGCPで共有されるため、自動的に従量制のBlazeプランになります。以下のような画面が表示されればプロジェクトの作成は完了です。

Firebaseとアプリを接続する

追加したFirebaseのプロジェクトとWebアプリを接続します。正確には、FirebaseのプロジェクトにWebアプリを追加するかたちになります。
まず、プロジェクト作成後の画面でスニペットマーク(ホバーすると「web」と表示されます)をクリックします。次に以下の画面になるので、アプリの名前を入力します。Firebase Hostingは後々設定を行うので、この段階ではしなくて大丈夫です。「アプリを登録」をクリックするとFirebaseとアプリを接続するためのスクリプトが表示されるので、それをアプリのbodyタグの下部に貼り付けます。別のjsファイルにしてbody下部で読み込むようにしてもいいと思います。

以下はスクリプトの例になります。

<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/8.2.6/firebase-app.js"></script>
<!-- TODO: Add SDKs for Firebase products that you want to use
     https://firebase.google.com/docs/web/setup#available-libraries -->
<script>
  // Your web app's Firebase configuration
var firebaseConfig = {
  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",
};
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
</script>

Firebase Authentificationを用いたユーザー認証

今回作成するアプリではユーザー登録が必要になります。Firebase Authentificationはユーザー認証を実装するためのBaaSで、面倒な認証機能の開発コストを大幅に削減することができます。様々なプロバイダーを用いた認証に対応しています! 今回は一番ベーシックなメールアドレスとパスワードを用いた認証を実装します。

まず、コンソールから手動でテストユーザーを作成します。Sign-in methodというタグを開いて、「メール/パスワード」を有効にします(今回はメールリンクによる認証は有効にしていません)。有効にすると、Usersというタブで手動でユーザーを追加できるので、テストユーザーを追加します。

ユーザーUIDはユーザーを識別するためのユニークなIDで、ユーザーを作成すると自動で生成されます。次にwebアプリからFirebase Authentificationを操作できるようにするために、先ほどbodyタグ下部に貼ったスクリプトにFirebase AuthentificationのSDKを追加します。

<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/8.2.6/firebase-app.js"></script>
<!-- TODO: Add SDKs for Firebase products that you want to use
     https://firebase.google.com/docs/web/setup#available-libraries -->
<!-- ↓ここに追加↓ -->
<script src="/__/firebase/8.2.3/firebase-auth.js"></script>
<script>
  // Your web app's Firebase configuration
var firebaseConfig = {
  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",
};
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
</script>

Webアプリ側からFirebase Authentificationが使えるかを確認します。ログインはsignInWithEmailAndPasswordにメールアドレスとパスワードを渡すだけでできます! 以下のemail、passwordに上記で作成したテストユーザーのメールアドレスとパスワードを入れて実行し、「ログインに成功!」が表示されればOKです。ユーザーの登録やログアウト、ログイン状態の確認なども同じ要領でできるのでとても楽です!!

var email = 'テストユーザーのメールアドレス';
var password = 'テストユーザーのパスワード';
firebase.auth().createUserWithEmailAndPassword(email, password)
  .then((user) => {
    // Signed in
    console.log('ログインに成功!')
  })
  .catch((error) => {
    var errorCode = error.code;
    var errorMessage = error.message;
    console.log(errorCode + errorMessage)
  });

Cloud Firestoreを用いたユーザーのデータ管理

Cloud Firestoreはクライアントアプリからリアルタイムにデータを同期します。また、NoSQLで正規化をしない設計なので、ネストしているデータなどもRDBよりも直感的に扱えるような気がします。

Firebase Authentificationと同様にコンソールから操作をします。まず、データベースの作成を選択します。次ににテストモードと本番環境モードを選択します。テストモードにすると、デフォルトでデータが誰でもアクセスできる状態で開始されますが、後々セキュリティルールを設定するのでテストモードで問題ありません。最後にロケーションの設定も求められますが、特別な理由がない限りはasia-northeast1(東京リージョン)で問題ないと思います。
データベースの作成ができたら、先ほど作成したテストユーザーのデータをデータベースに追加していきます。
今回はユーザーが複数のいきたい場所をリストとして持ち、ユーザーの名前や登録日なども記録したいので、データを擬似的に表すとだいたい以下のような感じになります。

{
    ユーザー1:{
        いきたいリスト:[
            いきたい場所1,
            いきたい場所2,
            いきたい場所3
        ],
        ユーザー名: test1,
        登録日: 2021年2月5日 12:37:57 UTC+9
    },
    ユーザー2:{
        いきたいリスト:[
            いきたい場所1,
            いきたい場所2,
            ・・・
        ],
        ユーザー名: test2,
        登録日: 2021年2月3日 12:37:57 UTC+9
    }・・・
}

このユーザー1、ユーザー2を扱うためにFirebase Authentificationでユーザーを追加した際に生成されたUIDを用います。
まず、「users」というコレクションを作成します。次にusersの配下のドキュメントにテストユーザーのUIDを追加します。

ドキュメントやコレクションについて詳しい説明は割愛しますが、コレクションはドキュメントの集合であり、ドキュメントは必ず一意な名前を持ちます。詳しくはこちらの記事にとてもわかりやすくまとまっています。擬似的に表すと以下のような感じです。ユーザーを示す一意な名前としてUIDを用いるということです。

{
    ユーザーの集合(コレクション):[
      ユーザー1のUID(ドキュメント),
      ユーザー2のUID(ドキュメント)
    ]
}

この要領で、ドキュメント配下に登録日として「created_at」、ユーザー名として「name」というフィールドと値を追加します。さらに、ドキュメント配下にはコレクションを置くことができます。これをサブコレクションといいます。ユーザー1のいきたいリストをコレクションにしたものを擬似的に表現すると以下のようになります。このようなコレクションをusersのドキュメント配下におきます。

{
    ユーザー1のいきたいリスト(コレクション):[
        いきたい場所1(ドキュメント),
        いきたい場所2(ドキュメント)
    ]
}

コレクションを開始というところから「posts」というコレクションを作成します。(いきたいリストもある意味投稿かなと思ったのでpostsにしましたが、微妙なネーミングかもしれないです)
先ほどと同じようにドキュメントを追加しますが、いきたい場所にはUIDのような一意なidはないので、「自動ID」というところを選択して一意な名前を自動生成します。「いきたい場所(必須)」、「ひとこと(任意)」、「場所に関連するURL(任意))」を保存したいので、「comment」と「place_name」と「url」というフィールドを作成しました。


これでテストユーザーのデータの登録ができたので、Firebase Authentificationと同様にWebアプリにCloud FirestoreのSDKを追加します。

<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/8.2.6/firebase-app.js"></script>
<!-- TODO: Add SDKs for Firebase products that you want to use
     https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="/__/firebase/8.2.3/firebase-auth.js"></script>
 <!-- ↓ここに追加↓ -->
<script src="/__/firebase/8.2.3/firebase-firestore.js"></script>
<script>
  // Your web app's Firebase configuration
var firebaseConfig = {
  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",
};
  // Initialize Firebase
  firebase.initializeApp(firebaseConfig);
</script>

Webアプリ側からCloud Firestoreを操作できるか確認します。
以下のコードではUIDを使ってドキュメントを取得し、さらに配下のサブコレクションを取得しています。uidをテストユーザーのUIDに置き換えて実行し、いきたいリストの情報が出力されればOKです。

var db = firebase.firestore();
var uid = 'テストユーザーのUID';
db.doc('users/' + uid).get().then((docSnapshot) => {
  var ikitaiList = [];
  docSnapshot.ref.collection("posts").get()
  .then((querySnapshot) => {
    querySnapshot.forEach((doc) => {
      var placeName = doc.data().place_name;
      var comment = doc.data().comment;
      var url = doc.data().url;
      var docId = doc.id;
      ikitaiList.push({
        'placeName':placeName,
        'comment': comment,
        'url': url,
        'docId': docId
      });
    });
    console.log(ikitaiList);
  })
  .catch((error) => {
    console(`いきたいリストの取得に失敗。${error}`)
  });
});

最後にセキュリティルールを更新します。
今回作成するアプリではデータの操作に以下のような制限をかける必要があります。

  • ユーザーの作成・閲覧はだれでもできる(ログイン中のユーザーが新たにユーザーを作成することは禁止してもいいかもしれない)
  • ユーザーを更新・削除は作成者のみ可能
  • いきたいリストの閲覧は誰でもできる
    これはCloud Firestoreの「ルール」というタブから簡単に設定することができます。今回必要な制限をかけるだけなら以下のような少ない記述で実現できます!

まとめ

ここまででFirebaseを使ってWebアプリをサーバーレスに作る準備ができました。後編ではアプリの実装に入ります。