Firestore Local Emulator Quickstart
本文是 Firestore Hands-on 操作路線 的基礎 lab。指令以 Firebase CLI 文件 與 Emulator Suite 文件 為準、最後檢查日 2026-06-16。
Firestore local emulator quickstart 的核心責任是建立後續 Security Rules 測試與 distributed counter lab 共用的本地環境。這個 lab 把 Firestore 從抽象服務轉成可觀察的 emulator、規則檔、seed 資料與 query 結果,全程不碰雲端專案。
本文的驗收標準是:你能在本地啟動 Firestore emulator、用 admin SDK 寫入並查詢一組 seed 資料、看到 emulator UI 裡的資料,並知道 cleanup 路徑。
Lab 環境與前置
Lab 在本地資料夾跑,需要 Node.js 與 Firebase CLI。以下命令建立一個可刪除的工作區並裝好工具。
1mkdir -p /tmp/firestore-lab
2cd /tmp/firestore-lab
3
4# Firebase CLI(已裝可跳過);用 npx 也可避免全域安裝
5npm install -g firebase-tools
6
7# 本 lab 的 Node 依賴
8npm init -y
9npm install firebase-adminemulator 需要 Java runtime(Firestore emulator 跑在 JVM 上)。java -version 確認存在;缺的話先裝 JDK 再繼續。驗收 artifact 是 /tmp/firestore-lab 工作區。
Emulator 設定
firebase.json 的核心責任是宣告要啟動哪些 emulator 與對應 port。這裡只開 Firestore 與 UI,不需要真實 Firebase 專案——emulator 用一個 demo project id 即可,demo- 前綴讓 CLI 知道這是純本地、不連雲端。
1cat > firebase.json <<'JSON'
2{
3 "emulators": {
4 "firestore": { "port": 8080 },
5 "ui": { "enabled": true, "port": 4000 }
6 },
7 "firestore": {
8 "rules": "firestore.rules"
9 }
10}
11JSONBaseline 規則
firestore.rules 的核心責任是定義授權。Quickstart 先用一組明確的 owner-scoped 規則(不是 allow read, write: if true,那是 deep article Case 1 的漏洞)。這份規則後續在 Security Rules test lab 會被測試覆蓋。
1cat > firestore.rules <<'RULES'
2rules_version = '2';
3service cloud.firestore {
4 match /databases/{database}/documents {
5 match /notes/{noteId} {
6 allow read: if request.auth != null
7 && resource.data.ownerId == request.auth.uid;
8 allow create: if request.auth != null
9 && request.resource.data.ownerId == request.auth.uid;
10 allow update, delete: if request.auth != null
11 && resource.data.ownerId == request.auth.uid;
12 }
13 }
14}
15RULES啟動 emulator
啟動 emulator 的核心責任是讓本地有一個可寫可查的 Firestore。用 demo project id 啟動,emulator UI 在 http://localhost:4000 可看到資料。
1firebase emulators:start --only firestore --project demo-firestore-lab這個指令會 foreground 跑住 emulator。保持它開著,另開一個 terminal 做 seed 與 query。終端輸出會印出 Firestore emulator 的位址(預設 localhost:8080)與 UI 位址。
Seed 資料(admin SDK 繞過規則)
Seed 的核心責任是建立可重跑的測試資料。admin SDK 連到 emulator 時繞過 Security Rules(模擬後端的特權寫入),適合種資料。關鍵是設 FIRESTORE_EMULATOR_HOST 環境變數——有了它,admin SDK 的寫入全部導向 emulator、不需要任何雲端 credential。
1cat > seed.js <<'JS'
2const admin = require('firebase-admin');
3admin.initializeApp({ projectId: 'demo-firestore-lab' });
4const db = admin.firestore();
5
6async function main() {
7 await db.collection('notes').doc('n1').set({
8 ownerId: 'alice', text: 'Alice first note', createdAt: Date.now(),
9 });
10 await db.collection('notes').doc('n2').set({
11 ownerId: 'bob', text: 'Bob first note', createdAt: Date.now(),
12 });
13 console.log('seeded 2 notes');
14}
15main().then(() => process.exit(0));
16JS
17
18# 在新 terminal、同 lab 目錄下
19export FIRESTORE_EMULATOR_HOST=localhost:8080
20node seed.js預期輸出 seeded 2 notes。打開 http://localhost:4000/firestore 應看到 notes collection 下兩筆 document。
Query baseline
Query 的核心責任是確認資料可讀、access pattern 入口可用。admin SDK 同樣繞過規則,這裡驗證的是資料與查詢本身(規則的放行 / 拒絕在下一個 lab 用 client context 驗)。
1cat > query.js <<'JS'
2const admin = require('firebase-admin');
3admin.initializeApp({ projectId: 'demo-firestore-lab' });
4const db = admin.firestore();
5
6async function main() {
7 const snap = await db.collection('notes')
8 .where('ownerId', '==', 'alice').get();
9 console.log(`alice notes: ${snap.size}`);
10 snap.forEach((d) => console.log(d.id, d.data().text));
11}
12main().then(() => process.exit(0));
13JS
14
15export FIRESTORE_EMULATOR_HOST=localhost:8080
16node query.js預期輸出 alice notes: 1 與 n1 Alice first note。這證明 where('ownerId', '==', ...) 的 access pattern 成立——它也正是 client 端要自帶、好讓 owner-scoped 規則放行的查詢條件。
Artifact 與驗收
| Artifact | 路徑 / 來源 | 驗收 |
|---|---|---|
| emulator config | firebase.json | Firestore + UI port 宣告 |
| 規則檔 | firestore.rules | owner-scoped、非 if true |
| seed 結果 | seed.js output + UI | notes/n1、notes/n2 存在 |
| query 結果 | query.js output | alice notes: 1 |
Cleanup
Cleanup 的核心責任是讓 lab 可重跑。emulator 的資料在 process 結束時預設不持久化(除非設了 --export-on-exit),所以停掉 emulator 等於清空資料。
1# 停掉 emulator:在 emulator terminal 按 Ctrl-C
2# 移除整個工作區
3rm -rf /tmp/firestore-lab若想保留 emulator 資料跨 session,啟動時加 --import=./data --export-on-exit=./data;lab 預設不持久化,保持每次乾淨起步。
完成本篇後,下一步進 Security Rules test lab(把上面的規則寫成自動化測試)或 Distributed counter lab。