學習Firebase Firestore語法(v9)

前言

因覺得開發一些部落格網站不太需要複雜的關聯式資料庫來存取使用者資料,所以學習一套NoSQL的資料庫,存取方式也是類似JSON的格式,所以學習起來十分快意。因為考慮到常常使用到一些常用的CRUD語法,所以寫一篇文件記錄起來,方便自己能夠快速查找!

留意:

  • v9版本就是應用到Firebase文件內的Web modular API,也就是利用前端操作資料庫,與Admin SDK和v8版本(Web namespaced API)語法存在一些差異。

  • 因為是前端操作資料庫,需要到專案設定>>一般設定,新增應用程式,選擇Web版本。並且要把Firestore Database裡的規則更改為以下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    rules_version = '2';

    service cloud.firestore {
    match /databases/{database}/documents {
    match /{document=**} {
    allow read, write: if true;
    }
    }
    }

    這樣才能讀寫資料庫,否則無權限會出現***Uncaught (in promise) FirebaseError: Missing or insufficient permissions.***的錯誤。
    但要留意,上述寫法代表讀寫全部開放,所以不安全,請以實際情況調整之。

  • 所有的動作都是非同步,所以在Javascript內必須做非同步處理,以下皆使用async/await示範!

  • 初始化資料庫,並取得db物件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import { initializeApp } from "firebase/app";
    import { getFirestore } from "firebase/firestore";
    const firebaseConfig = {
    apiKey: "xxxxxxx",
    authDomain: "xxxxxxx",
    projectId: "xxxxxxx",
    storageBucket: "xxxxxxx",
    messagingSenderId: "xxxxxxx",
    appId: "xxxxxxx",
    };

    const app = initializeApp(firebaseConfig);

    const db = getFirestore(app);

  • 接下來以CRUD順序介紹

    • C-(Create)
    • R-(Read)
    • U-(Update)
    • D-(Delete)

C-(Create)創建資料

  • 創建文件(使用setDoc,可命名文件名)
    語法
    1
    await setDoc(doc(db, "集合名", "文件名"), 物件資料);
    如果文件不存在則創建,若存在則整個物件覆蓋;若不想整個物件覆蓋,只要修改則要加上參數物件{ merge: true }
    語法
    1
    await setDoc(doc(db, "集合名", "文件名"), 物件資料, { merge: true });
    範例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import { doc, setDoc } from "firebase/firestore"; 

    (async () => {
    await setDoc(doc(db, "cities", "LA"), {
    name: "Los Angeles",
    state: "CA",
    country: "USA"
    }, { merge: true });
    })();
  • 創建文件(使用addDoc,Firebase自動命名文件名)
    語法
    1
    await addDoc(collection(db, "集合名"), 物件資料);
    範例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import { collection, addDoc } from "firebase/firestore"; 

    (async () => {
    const docRef = await addDoc(collection(db, "cities"), {
    name: "Tokyo",
    country: "Japan",
    });
    console.log("Document written with ID: ", docRef.id);
    })();

    可以取得創建文件的id

R-(Read)查詢資料

  • 查詢文件
    語法
    1
    await getDoc(doc(db, "集合名", "文件名"))
    範例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import { doc, getDoc } from "firebase/firestore";

    (async () => {
    const docSnap = await getDoc(doc(db, "cities", "LA"));
    if (docSnap.exists()) {
    console.log("Document data:", docSnap.data());
    } else {
    console.log("No such document!");
    }
    })();
    使用.data(),取得資料
  • 從集合查詢多個文件
    語法
    1
    2
    const q = query(collection(db, "集合名"));
    const querySnapshot = await getDocs(q);
    範例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import { collection, query, where, getDocs } from "firebase/firestore";

    (async () => {
    const q = query(collection(db, "cities"));
    const querySnapshot = await getDocs(q);

    for (const doc of querySnapshot.docs) {
    console.log(doc.id, " => ", doc.data());
    }
    })();
    使用for…of,必須針對querySnapshot再加上.docs跑迴圏(如使用forEach則直接使用querySnapshot)。最後再使用.data(),取得資料。

U-(Update)更新資料

  • 更新文件(使用updateDoc,非整個物件覆蓋)
    語法

    1
    await updateDoc(doc(db, "集合名", "文件名"), 物件資料);

    範例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import { doc, updateDoc } from "firebase/firestore";

    (async () => {
    await updateDoc(doc(db, "cities", "LA"), {
    name: "Los Angeles",
    state: "CA",
    country: "new country!",
    });
    })();
  • 更新文件(使用setDoc,整個物件覆蓋全部文件內容)
    語法

    1
    await setDoc(doc(db, "集合名", "文件名"), 物件資料);

    若不覆蓋,務必加上, { merge: true }
    語法

    1
    await setDoc(doc(db, "集合名", "文件名"), 物件資料, { merge: true });

    範例
    同本文創建文件setDoc方法

    *小提醒:使用updateDoc等同於setDoc加上{ merge: true },效果完全相同

D-(Delete)刪除文件

語法

1
await deleteDoc(doc(db, "集合名", "文件名"));

範例

1
2
3
4
5
import { doc, deleteDoc } from "firebase/firestore";

(async () => {
await deleteDoc(doc(db, "cities", "LA"));
})();

小結

透過紀錄語法,讓以後的自己方便查找資訊,前端撈取資料雖然方便,但有時候要留意安全性問題!

參考資料
Firebase官方文件