Firebaseを始めよう

フロントエンドを初めて実際にプロダクトをつくろうと思って始めたけど、バックエンドのテクノロジーに圧倒されていませんか?またフロントエンドの認証システムとバックエンドの認証システムを連携させる方法が難しいと思いませんか?

そんな問題を解決してくれるのがGoogleが提供するFirebaseです。今日は、無料プランでDBを作成して実際にコードを書いてみます。

ちなみにFirebaseはトラディショナルなリレーショナルデータベース(MySQL,PostgreSQLなど)とは違うNoSQLデータベース(MongoDBなど)のデータベースを提供しています。NoSQLを使用する良い機会になるので是非試してみましょう!

Firebaseとは

Google Cloud Console(グーグルクラウドコンソール)上に作成されたバックエンドサービス(Baas:Backend as a Service)のことです。では、最初にFirebaseのデータベース(Realtime Database)でできることを紹介します。この他にFirestore Databaseという最新のデータベースサービスがありますが、それは後程紹介します。

Firebase Realtime Databaseリアルタイムでデータを保存してユーザー間で同期できる、クラウドホスト型 NoSQL データベースです。 新機能のCloud Firestore なら、グローバル スケールでアプリデータを保存、同期、クエリできます。

デバイス間の連携が容易:リアルタイム同期により、ユーザーはあらゆるデバイス(ウェブ、モバイル)からデータに簡単にアクセスして、他のユーザーとの共同作業を効率的にできるようになります。

サーバー不要のアプリを構築:Realtime Database にはモバイルとウェブの SDK が含まれているため、サーバーがなくてもアプリを構築できます。Cloud Functions for Firebase を使用して、データベースでトリガーされるイベントに応答するバックエンド コードを実行することもできます。

オフラインでの使用に最適:ユーザーがオフラインになると、Realtime Database SDK はデバイス上のローカル キャッシュを使用してデータを表示し、変更を保存します。デバイスがオンラインになると、ローカルデータが自動的に同期されます。

高度なユーザーベースのセキュリティ:Realtime Database と Firebase Authentication の統合により、デベロッパーはシンプルかつ直感的な認証を利用できます。Google の宣言型セキュリティ モデルを使用して、ユーザー ID、またはデータのパターン マッチングによってアクセスを許可できます。

これで開発のスピードがあげられそうですね!

そのほかに電話番号での認証やGoogleのアカウントを使っての認証も簡単に作成できてしまいます。

FirebaseではWebのみならずiOSやAndroidのアプリも作成できます。今回はWebに絞って紹介していきます。

Firebaseの料金

料金についてはこちらのリンクを参照してください。

まずは、無料プランからサービスを検証してみましょう。ではすぐに利用開始のボタンを押します。

Firebaseのプロジェクトを作成

Firebaseのプロジェクトを作成するとGoogle Cloud Consoleに作成したプロジェクトが追加されます。

では早速プロジェクトを追加します。

ではフォームに従いプロジェクト名を作成して続行します。

次にGoogleアナリティクスを追加するか設定します。テストの場合は不要ですが、実際にアプリを公開する場合はあった方が良いですね。

ではGoogle アナリティクスのプロジェクトを連携させて完了です。

これでFirebaseのバッシュボードが見れるようになりましたね。

Realtime DatabaseとFirestore Database

Firebaseのダッシュボードから2種類のデータベースを作成することができます。Firestore Databaseは最新のデータベースのサービスになります。その違いを見てみます。以下、Firebaseの公式サイトより引用。

データモデル

Realtime Database と Cloud Firestore はどちらも NoSQL データベースです。

Realtime DatabaseCloud Firestore
データを 1 つの大きな JSON ツリーとして保存します。シンプルなデータは非常に簡単に保存できます。複雑で階層的なデータについては、大規模な整理を行うことが難しいです。Realtime Database データモデルの詳細をご覧ください。データをドキュメントのコレクションとして保存します。シンプルなデータはドキュメントに保存するのが簡単です。これは JSON と非常によく似ています。複雑で階層的なデータについては、ドキュメント内のサブコレクションを使用することで、大規模な整理を簡単に行うことができます。非正規化とデータの平坦化が少なくて済みます。Cloud Firestore データモデルの詳細をご覧ください。

リアルタイムおよびオフライン サポート

どちらのデータベースもモバイル向けのリアルタイム SDK を備えており、オフライン対応アプリのローカルデータ ストレージに対応しています。

Realtime DatabaseCloud Firestore
Apple、Android の各クライアント向けのオフライン サポート。Apple、Android、ウェブの各クライアント向けのオフライン サポート。

プレゼンス

クライアントがオンラインかオフラインかを知るために役立ちます。Firebase Realtime Database ではクライアントの接続ステータスを記録し、クライアントの接続状態が変化するたびに更新できます。

Realtime DatabaseCloud Firestore
プレゼンスをサポート。ネイティブではサポートされません。Cloud Functions を使用して Cloud Firestore と Realtime Database を同期することで、Realtime Database でのプレゼンスに対するサポートを活用できます。Cloud Firestore でプレゼンスを構築するをご覧ください。

クエリ

いずれのデータベースもクエリによってデータの取得、並べ替え、フィルタリングを行うことができます。

Realtime DatabaseCloud Firestore
制限付きの並べ替えとフィルタリング機能を備えた多層型クエリ。クエリでは、プロパティの並べ替えまたはフィルタリングを行うことはできますが、両方はできません。クエリはデフォルトで多層型です。常にサブツリー全体を返します。クエリは、JSON ツリー内の個々のリーフノード値まで、あらゆる粒度でデータにアクセスできます。クエリはインデックスを必要としません。ただし、データセットが大きくなるにつれて、特定のクエリのパフォーマンスは低下します。複合型の並べ替えとフィルタリング機能を備えたインデックス付きクエリ。1 つのクエリで、フィルタをつなぎ合わせたり、プロパティのフィルタリングと並べ替えを組み合わせたりすることができます。層が少ないクエリは、特定のコレクションまたはコレクション グループ内のドキュメントのみを返し、サブコレクション データを返しません。クエリは常にドキュメント全体を返す必要があります。クエリはデフォルトでインデックス付きになります。クエリのパフォーマンスは、データセットではなく、結果セットのサイズに比例します。

書き込みとトランザクション

Realtime DatabaseCloud Firestore
基本的な書き込みオペレーションとトランザクション オペレーション。設定オペレーションと更新オペレーションを通じてデータを書き込みますトランザクションは、特定のデータ サブツリーに対してアトミックです。高度な書き込みオペレーションとトランザクション オペレーション。設定オペレーションと更新オペレーションとともに、配列や数値の演算子などの高度な変換を通じてデータ オペレーションを書き込みますトランザクションは、データベースの任意の部分からアトミックにデータを読み書きできます。

信頼性とパフォーマンス

Realtime DatabaseCloud Firestore
Realtime Database はリージョン単位のソリューションです。リージョン構成で使用できます。データベースは、リージョン内のゾーンの可用性によって制約を受けます。レイテンシが非常に低く、頻繁な状態同期に最適です。Realtime Database のパフォーマンスと信頼性の特性については、サービスレベル契約をご覧ください。Cloud Firestore は自動的にスケールするリージョンまたはマルチリージョンのソリューションです。異なるリージョンにある複数のデータセンターにデータを格納し、グローバルなスケーラビリティと信頼性を確保します。世界各地のリージョンまたはマルチリージョン構成で利用できます。Cloud Firestore のパフォーマンスと信頼性の特性については、サービスレベル契約をご覧ください。

スケーラビリティ

Realtime DatabaseCloud Firestore
スケーリングにはシャーディングが必要です。1 つのデータベースで、約 200,000 件の同時接続と、毎秒 1,000 回の書き込みまでスケーリングが可能です。それ以上スケーリングするには、複数のデータベースにまたがるデータのシャーディングが必要になります。個々のデータへの書き込みレートに対するローカルな制限はありません。スケーリングは自動で行われます。完全な自動スケーリングに対応しています。現在のところ、約 100 万件の同時接続と、毎秒 10,000 回の書き込みまでのスケーリング制限があります。将来的には、これらの制限を引き上げる予定です。個々のドキュメントまたはインデックスへの書き込みレートに対する制限があります。

セキュリティ

Realtime DatabaseCloud Firestore
承認と検証を分離するカスケード ルール。Realtime Database セキュリティ ルールで保護されたモバイル SDK からの読み取りと書き込み。読み取りルールと書き込みルールはカスケード式に適用されます。validate ルールを使用して個別にデータを検証します。承認と検証を組み合わせた非カスケード ルール。Cloud Firestore セキュリティ ルールで保護されたモバイル SDK からの読み取りと書き込み。Identity and Access Management(IAM)で保護されたサーバー SDK からの読み取りと書き込み。ワイルドカードを使用する場合を除き、ルールがカスケード式に適用されることはありません。ルールでクエリを制約できます。クエリの結果にユーザーがアクセスできないデータが含まれている場合、クエリ全体が失敗します。

料金

どちらのソリューションも Spark と Blaze の料金プランで利用可能です。

Realtime DatabaseCloud Firestore
帯域幅とストレージにのみ課金されますが、課金レートは高くなります。Realtime Database の料金プランの詳細をご覧ください。主な課金対象は、データベースで実行されているオペレーション(読み取り、書き込み、削除)、帯域幅、ストレージです。帯域幅とストレージへの課金レートは低く設定されています。Cloud Firestore は、App Engine プロジェクトの 1 日の使用量上限に対応しており、お客様が設定したコストを超過することはありません。Cloud Firestore の料金プランの詳細をご覧ください。

Cloud Firestore と Realtime Database の使用

単一の Firebase アプリまたはプロジェクト内で両データベースを並行して使用することができます。いずれの NoSQL データベースも保存できるデータタイプは同じであり、クライアント ライブラリの動作もほぼ同じです。アプリで両方のデータベースを使用する場合には、前述の相違点に注意してください。

どちらのデータベースを使用すべきか

Frebaseでは2種類のデータベースが提供されており使い方自体はほぼ同じになりますが、提供されるサービスが異なります。下記のどちらに当てはまるかを参考にして最適のデータベースを活用しましょう。

Realtime Database:

  • データの構成がシンプル、もしくはヒエラルキーでない。(階層のあるツリー型のデータ)
  • 一つのデータベースに同時接続するユーザー(アクセス)が100,000以下、1000書き込み/秒以下
  • 課金制度が全体のネットワーク使用量のみで計算される

Cloud Firestore database

  • データが複雑、もしくはツリー型(ヒエラルキー)の構成の場合。
  • 一つのデータベースに同時接続するユーザー(アクセス)が100,000以上
  • 細かい料金設定や制限ができる。

料金の計算

もし、具体的な料金を借りで計算したい場合はFirebaseの公式サイトで確認することができます。

CloudFirestoreのデータベースを作成

では上記の比較表を確認したところで、参考例としてCloud Firestoreのデータベースを作成しましょう。

データベースの作成ボタンかたデータベースを作成します。

今回はテストモードを選択します。

サーバーのロケーションを作成します。

主なアクセスが日本を想定する場合は東京を選択します。

ではコレクションを開始から新しいデータのキーを作成しましょう。

例としてコレクションIDにuser(ユーザー)を作成します。

次にユーザーデータで使いそうなキー、バリューを作成します。

これで保存をクリックします。これらはNoSQLのデータベースになるので名称がコレクション(DBのテーブル) ドキュメント(レコード)に変更されます。

クライアントからデータにアクセス

では、データができたところで、FirebaseのJavaScriptライブラリを使って自身のネットワークからこのデータにアクセスしてみましょう。

今回はウェブからアクセスするのでウェブSDKを使用します。

まずは、firebaseのダッシュボードのProject Overviewをクリックして下記の画像のようにウェブようにアプリをセットアップします。</>のボタンを押してください。

次にアプリ名を登録します。

アプリ自体のホスティングは後からできますし他のサーバーからホスティングしても良いのでスキップします。

この後にJavaScriptライブラリをインストールしていきます。

プロジェクトの作成

今回はViteを使ってVanilla JavaScriptのみでプロジェクトを作成します。詳しいViteの使い方はこちらの記事で説明しています。

npm create vite@latest

✔ Project name: … fire-app
✔ Select a framework: › Vanilla
✔ Select a variant: › JavaScript

Scaffolding project in /home/dan/Documents/fire-app...

Done. Now run:

  cd fire-app
  npm install
  npm run dev

ではコマンドに従いディレクトリを変更しnpm i でイニシャライズします。

次にFirebaseのライブラリをインストールします。

npm i firebase

#テキストエディタを開きます。
code .

次に、先ほどのFirebaseで手に入れたコード参照してください。

では新しくfirebase.jsを作成してこのようにペーストします。※コンフィグはデプロイの際には.envファイルなどに格納しておきましょう。

その間にfirebaseのデータベースに戻りサンプルのデータをいくつか追加しておきましょう。

firebase.js

// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
  apiKey: "AIzaSyAuZgjd80c8oNquonvmuMMILrDcb3eDUhE",
  authDomain: "asameshicode-2ae0d.firebaseapp.com",
  databaseURL: "https://asameshicode-2ae0d-default-rtdb.asia-southeast1.firebasedatabase.app",
  projectId: "asameshicode-2ae0d",
  storageBucket: "asameshicode-2ae0d.appspot.com",
  messagingSenderId: "772924398849",
  appId: "1:772924398849:web:91d359df720398f80acf00",
  measurementId: "G-EN8K1E4KX8"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);

では、実際にuserデータをフェッチしてみます。

import { initializeApp } from "firebase/app";
import {
  getFirestore,
  collection,
  getDocs,
  Firestore,
} from "firebase/firestore";
// import firebase from "firebase/compat/app";

const firebaseConfig = {
  apiKey: "AIzaSyAuZgjd80c8oNquonvmuMMILrDcb3eDUhE",
  authDomain: "asameshicode-2ae0d.firebaseapp.com",
  databaseURL:
    "https://asameshicode-2ae0d-default-rtdb.asia-southeast1.firebasedatabase.app",
  projectId: "asameshicode-2ae0d",
  storageBucket: "asameshicode-2ae0d.appspot.com",
  messagingSenderId: "772924398849",
  appId: "1:772924398849:web:91d359df720398f80acf00",
  measurementId: "G-EN8K1E4KX8",
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

let userList = [];
async function getUsers(db) {
  const usersCol = collection(db, "user");
  const userSnapshot = await getDocs(usersCol);
  userList = userSnapshot.docs.map((doc) => doc.data());
  return userList;
}

getUsers(db).then((userList) => {
  console.log(userList);
//{name: 'Boss', psssword: 'Strong'}
//{name: 'Choco', passowrd: 'oishi'}
//{is_admin: true, password: 'Himitsu', name: 'Yoshi'}
});

このようにgetUsers関数で実際にFirebaseのデータを呼び出すことができました。

次にフェッチしたデータをHTMLにレンダーしてみます。

import { initializeApp } from "firebase/app";
import {
  getFirestore,
  collection,
  getDocs,
  Firestore,
} from "firebase/firestore";
// import firebase from "firebase/compat/app";

const firebaseConfig = {
  apiKey: "AIzaSyAuZgjd80c8oNquonvmuMMILrDcb3eDUhE",
  authDomain: "asameshicode-2ae0d.firebaseapp.com",
  databaseURL:
    "https://asameshicode-2ae0d-default-rtdb.asia-southeast1.firebasedatabase.app",
  projectId: "asameshicode-2ae0d",
  storageBucket: "asameshicode-2ae0d.appspot.com",
  messagingSenderId: "772924398849",
  appId: "1:772924398849:web:91d359df720398f80acf00",
  measurementId: "G-EN8K1E4KX8",
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

let userList = [];
async function getUsers(db) {
  const usersCol = collection(db, "user");
  const userSnapshot = await getDocs(usersCol);
  userList = userSnapshot.docs.map((doc) => doc.data());
  return userList;
}

const userName = document.querySelector("#userName");
function renderUser(doc) {
  let li = document.createElement("li");
  let name = document.createElement("span");
  console.log(doc);
  name.textContent = doc.name;
  li.appendChild(name);
  userName.appendChild(li);
}

getUsers(db).then((userList) => {
  userList.forEach((user) => {
  renderUser(user);
  });
});

app:アプリをイニシャライズしています。

db:firebaseのデータベースを定義します。

userList:データベースのユーザーのドキュメント(データ)を格納する変数です。

userName:index.htmlの#appを#userNameに変えて取得できるようにしました。

getUsers:firebaseのデータベースに行き、ユーザーデータを取得してuserListにデータを格納します。

最後にgetUsers関数を実行するようにします。

では実際にサーバーを起動してみてみます。

npm run dev

これで実際にfirebaseのデータベースからデータをフェッチしてHTMLにレンダーできることが確認できました。

実際にはReactやVue、Angularなどのフレームワークを使って構築することになりますが基本を知っておくことは大切なのでVanilla JSでも書けるようにしておきましょう。

次回以降はSPAのVueを使ってGoogleアカウントでログインするシステムを構築してみたいと思います。

お疲れ様でした。