2021/10/22

Firebaseの勉強

@ 古曳 純基

Firebase

この記事はFirebaseの内容をまとめたノートです。
一応、本も参考にしてますが、俺の頭の中のfirebaseをアウトプットするだけの回なので見たい人は勝手にどうぞって感じ。

参考文献は以下になります。

俺の中のFirebase

おれのなかのFirebaseのイメージを以下にまとめたいと思います。
赤いところをこの記事では紹介しようと思います。(気が向けば、また記事を書きます。特にコマンドクエリ責任分離や複合インデックスについては概念を抑えておきたいので記事を書くと思います。)

  • フロントからの直接アクセス
  • データの操作はCRUD
  • コレクション(コレクションはドキュメントを束ねるもの。ドキュメントが作成されてたら生まれる。ドキュメントがひとつも無くなったら消える)
  • ドキュメント(ドキュメントはデータが書かれているもの、JSON形式で書かれている)
  • サブコレクション(ドキュメントの中に存在するコレクションのこと)
  • コレクショングループ(同じIDを持つコレクションを同一のコレクションとしてみなす。再帰ワイルドカードの登場でセキュリティルールも書きやすい)
  • クエリ:データベースに対する問い合わせのこと(この条件に合うデータをちょうだいってやつ)
  • 直接アクセスが危険なのでセキュリティールールを適用
    • 誰が(ユーザー認証):Authentication
    • どんなデータを操作するのか
      • スキーマ検証(Firebaseはスキーマレスな構造ではあるが、同じコレクション内のドキュメントの構造が全く違っていたら管理しにくい)
      • データのバリデーション(バリューの文字数、正規表現による禁句の指定、年齢 etc ...)
  • リソースドリブン(操作するデータが主体となっている)
  • 豊富なクラウドファンクション(バックグランド関数や呼び出し可能な関数)
  • アトミックオペレーション
    • トランザクション(冪等な処理をadminを用いて行う、admin処理なのでセキュリティルールはないけど強い整合性を生み出すことが可能)
    • バッチ処理(一括書き込みの話、ルールがちょっと特殊)
    • フィールドバリュー(軽い数値計算くらいならこれで十分)
  • カスタムクレーム(authenticationにデータを組み込める領域が存在する
  • コマンドクエリ責任分離(firestoreの読み取りと更新の操作を分離するやつ)
  • 複合クエリ(クエリを発行する際に、複雑なフィルターを作成すると必要なやつ)


今回は、この辺を思い出すのが精一杯でした。
それでは、上記の内容について説明していきます。

データの操作

データの操作は以下の4つがあります。

  • create
  • update
  • delete
  • read


create

データの作成は、ドキュメントIDを指定する場合と指定せずに自動採番してもらう方法があります。
ID指定してドキュメントを作成する方法

import { doc , setDoc , TimeStamp } from "firebase/firestore";

const userReference = doc(db,"users", "Kohiki")
// データの書き込み=通信を挟む=非同期処理
await setDoc(userReference, {
  name: "Kohiki Junki",
  age: 18
});


IDを指定せずに作るパターン

import { colection. addDoc } from "firebase/firestore";

const userReference = addDoc(collection(db, "users"), {
  name: "annonymous",
  age: null
});


簡単やな

update

データの更新になります。

import { doc. updateDoc } form "firebase/firestore";

const userRef = doc(db, "users" , "Kohiki")
await updateDoc(userRef, {
  age: 19
});


ちょいとサーバータイムスタンプも学習しておきますか。
firebaseでは、サーバータイムスタンプなるものを使用するケースもあります。これはセキュリティルールなどで必要になることもあります。

import { updateDoc , serverTimestamp } from "firebase/firestore";

const docRef = doc(db  . "objects" , "id")
const updateTimestamp = await updateDoc(userRef , {
 serverTimestamp: serverTimestamp()
});


これまでは、FieldValueを使用していましたが、serverTimestampという関数をインポートすればいい感じに処理してくれるみたいですね。

read

データの取得は以下のようになるらしい(公式ドキュメントより)

import { doc , getDoc } from "firebase/firestore";

const docSnap = await getDoc(doc(db, "users" , "Kohiki"));

if (!docSnap.exist())return;
console.log(docSnap.data.name);

// Kohiki Juki


ゲットオプションは以下のようになります。

  • デフォルトでは、サーバーからの読み込み
  • getDocFromCacheにより、キャッシュ優先読み取りが可能っぽい


以下にキャッシュ優先読み取りの例を示す

imort { doc , getDoc , getDocFromCache } from "firebase/firestore";

const docRef = doc(db, "user" , "Kohiki");

// try chatch構文で書くみたいです
try{
  const userDoc = await getDocFromCache(docRef);
  if (!userDoc.exist()){
    const userDoc = await getDoc(docRef);
  }
}catch((error)=>{
  console.log("this is error");
})


みたいな感じかな。違うかも(自分の思考実験を書いただけです)

ちなみに、とってきたドキュメントスナップショットには以下のフラグも含まれています。

  • fromCache:キャッシュされたデータを持つ場合は、trueとなります。
  • hasPendingWrite : スナップショットの中にバックエンドに書き込まれていないデータが含まれるのかどうかを示すフラグ(リスナーは、バックエンドへの書き込みが完了した時点でhasPendingWeite=falseを持つスナップショットを受け取ります。)

キャッシュから読み込んだデータは無視するようなコードを書いてみましょう。キャッシュからのコードを無視することで、オフライン対応に弱くなりますが、強い整合性を担保することが可能になります。

import { doc , getDoc } from "firebase/firestore";

const docSnapshot = await getDoc(doc(db, "users" , "Kohiki");
// 早期リターン
if (docSnapshot.metadata.fromCache)return;

console.log(`I'm ${docSnapshot.data().age} years old !");

delete

最後にドキュメントの削除について書いてこの記事は終わります。

import { doc , deleteDoc } from "firebase/firestore";

// 匿名アカウントの情報を削除します
await deleteDoc(doc(db, "users" , "annonymous");


以上や

© 2021 powerd by UnReact