BASHCAT
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- title: Firebase 無主機開發 2026 完全指南:從 Cloud Functions 到 App Hosting,少寫一行伺服器配置的工程美學 description: 工程主管必讀的 Firebase 架構評估指南。涵蓋 Cloud Functions 2nd Gen、Firestore Security Rules、App Hosting、分片計數器、真實成本計算與 Supabase 對戰。 keywords: Firebase, 無主機開發, Serverless, Cloud Functions, Firestore, App Hosting, Supabase, 後端即服務 date: 2026-05-05 --- # Firebase 無主機開發 2026 完全指南:從 Cloud Functions 到 App Hosting,少寫一行伺服器配置的工程美學 我前陣子聽到一個案例:某家 SaaS 公司用 Firebase 開發 MVP,產品起飛後幾個月,使用者來到 1,000 萬,月帳單從 $1,200 飆到 $30,000,而且**沒有任何告警**,直接收到一張五位數帳單。最後他們花了三個月把後端整個改寫到 Supabase,月帳單回到 $25 ([Horizon Dev 2026 比較報告](https://horizon.dev/blog/supabase-vs-firebase-startups/) )。 這個故事我不是要嚇你「Firebase 不能用」,而是想說:**無主機(Serverless)這條路,不是只有 `firebase deploy` 就萬事 OK。** Firebase 在 2026 年仍然是 BaaS 領域最完整的方案之一——根據 [Tech Insider 2026 對比報告](https://tech-insider.org/supabase-vs-firebase-2026-2/) 與 [Horizon Dev 比較](https://horizon.dev/blog/supabase-vs-firebase-startups/) ,weekly npm downloads 約 320 萬,遙遙領先 Supabase 的 45 萬。但它的計費模式、Vendor Lock-in 風險、Firestore 與 SQL 思維的差異,每一項都可能讓沒做好功課的團隊吃苦頭。 這篇文章把官方文件、生產環境踩坑、2026 年最新定價、AI 整合(Genkit、Vertex AI)、和 Supabase 的最新對比通通整理在一起,讓你看完後能做出明確的架構決策——不只是「跟風用」,而是**用得對、用得久、用得不爆預算**。 **本文前置知識**:你需要對 Node.js / TypeScript、基礎雲端概念(容器、CDN、CI/CD)有一定理解。文中所有規格與定價以 2026 年 5 月官方文件為準。 --- ## 一、什麼是 Firebase 無主機?拆解五層核心服務 ![firebase-無主機-五層架構](https://hackmd.io/_uploads/HkFiuZDAZe.jpg) 「Serverless」字面意思是「沒有伺服器」,但這顯然不對——程式碼總得跑在某個地方。它的真正意涵是:**伺服器的存在對你透明**,你不用 SSH、不用 patch OS、不用設 nginx,雲端會在請求進來的瞬間動態分配資源,閒置時就回收。 Firebase 的無主機體系由五個核心組件構成: | 組件 | 角色 | 主要負責 | | --- | --- | --- | | **Cloud Functions for Firebase** | 運算層 | 執行後端邏輯、響應事件或 HTTPS 請求 | | **Cloud Firestore / Realtime Database** | 資料層 | NoSQL 儲存與多端即時同步 | | **Firebase Authentication** | 安全層 | 註冊登入、社群登入、多因素驗證 | | **Firebase Hosting / App Hosting** | 部署層 | 靜態託管、SSR 應用、CDN 分發 | | **Cloud Storage for Firebase** | 儲存層 | 圖片、影片等大型檔案 | 從架構視角看,這五層的關係是這樣的: ```mermaid flowchart LR Client[Web / Mobile Client] -->|HTTPS| Hosting[Firebase Hosting / App Hosting] Client -->|SDK| Auth[Firebase Authentication] Client -->|SDK + Rules| FS[Cloud Firestore] Client -->|SDK| Storage[Cloud Storage] Hosting --> CF[Cloud Functions] Auth -.事件.-> CF FS -.事件.-> CF Storage -.事件.-> CF CF --> FS CF --> Storage ``` 最關鍵的設計選擇在於:**客戶端 SDK 直接打資料庫**,不需要中間人(傳統的後端 API 伺服器)。這帶來兩個極端: - **好處**:開發者不用為每個 CRUD 寫 REST endpoint,前端工程師也能完成原本要兩個團隊才做得完的事 - **壞處**:所有的權限驗證、資料 schema 防護,全靠 Security Rules 來把關。寫不好就是巨大的安全洞 每一層都有自己的 know-how。我們從跑邏輯的 Cloud Functions 開始拆。 --- ## 二、Cloud Functions 1st vs 2nd Gen:終於不用為冷啟動煩惱了 ![firebase-cloud-functions-1代vs2代](https://hackmd.io/_uploads/Skg2O-PRWg.jpg) Cloud Functions for Firebase 在 2022 年底推出第二代(2nd Gen),但很多 2020 年前的 Firebase 專案到今天還在跑 1st Gen——這幾乎可以說是「在用古董機種」。兩代差異不只是版本號,根本是不同層級的東西。 ### 規格對比 引用自 [官方 Firebase 版本對比文件](https://firebase.google.com/docs/functions/version-comparison) 與 [oneuptime 2026 v2 評測](https://oneuptime.com/blog/post/2026-02-17-how-to-use-firebase-cloud-functions-v2-with-cloud-run-under-the-hood/view) : | 規格 | 1st Gen | 2nd Gen | | --- | --- | --- | | HTTP timeout | 9 分鐘(540s) | **60 分鐘(3600s)** | | 事件觸發 timeout | 9 分鐘 | 9 分鐘 | | 最大記憶體 | 8 GB | **16 GiB(GA)** | | 最多 vCPU | 2 | **4(GA)** | | Concurrency | **1 req/instance** | **預設 80,可設 1–1000** | | 底層基礎 | Cloud Functions 原生 | **Cloud Run + Eventarc** | | Min instances | 支援 | 原生支援 | | Traffic splitting | 不支援 | 支援 | 最關鍵的差異是 **concurrency**。在 1st Gen 中,每個實例只能處理一個請求——意思是當 50 個請求同時湧入,系統就得啟動 50 個新實例,每個都要付出冷啟動代價(約 100ms 到數秒)。 但在 2nd Gen 中,[一個實例可以同時處理最多 1000 個請求](https://firebase.blog/posts/2022/12/cloud-functions-firebase-v2/) 。這意味著突發 50 個請求**完全不會觸發冷啟動**——一個實例就吃下了。 ### 程式碼遷移範例 從 1st Gen 改寫到 2nd Gen,import 路徑是關鍵: ```javascript // 1st Gen — 舊版 const functions = require("firebase-functions/v1"); exports.helloWorld = functions.https.onRequest((req, res) => { res.send("Hello from 1st Gen"); }); ``` ```javascript // 2nd Gen — 新版,加上 concurrency 設定 const { onRequest } = require("firebase-functions/v2/https"); exports.helloWorld = onRequest({ concurrency: 500, // 單一實例最多並行 500 請求 minInstances: 1, // 保持 1 個實例預熱,徹底消滅冷啟動 cpu: 1, memory: "512MiB", timeoutSeconds: 60, }, (req, res) => { res.send("Hello from 2nd Gen"); }); ``` ### 冷啟動三招實戰 冷啟動是無主機的「原罪」,但 2nd Gen 加上幾個技巧後幾乎可以忽略: **1. 設置 minInstances** 對延遲敏感的函數(用戶登入、結帳),保留 1–2 個實例預熱。會多付一點錢,但首次請求延遲從 2 秒降到 50ms。 **2. 延遲載入大型依賴** 分析 Java Code Geeks 的 [冷啟動深度報告](https://www.javacodegeeks.com/2025/04/comprehensive-analysis-of-firebase-functions-cold-starts.html) 指出,JavaScript 函數的啟動時間幾乎全花在 `require()`/`import`: ```javascript // ❌ 全域載入:每次冷啟動都吃 800ms const { BigQuery } = require("@google-cloud/bigquery"); exports.report = onRequest((req, res) => { const bq = new BigQuery(); // ... }); // ✅ 路徑內載入:只有實際用到時才載入 exports.report = onRequest((req, res) => { if (req.path === "/heavy-report") { const { BigQuery } = require("@google-cloud/bigquery"); const bq = new BigQuery(); // ... } }); ``` **3. 用 onInit() 推遲全域初始化** 全域變數的初始化會在每次冷啟動時執行。把昂貴的初始化(連線池、SDK 客戶端)放進 `onInit()` hook,避免部署時 timeout。 > **Tip**:1st Gen 還在運作,但 [Google 已宣告 2025/02/18 起 1st Gen 強制使用 Artifact Registry](https://community.flutterflow.io/ask-the-community/post/firebase---migrate-your-cloud-run-functions-1st-generation-to-artifact-rnyX3ugOEVkuaNU) 。新專案請直接從 2nd Gen 開始。 --- ## 三、Firestore Security Rules:一張規則表救你十條 API ![firebase-security-rules-安全層](https://hackmd.io/_uploads/SkunuWvCZx.jpg) 無主機架構下最大的安全爭議是「客戶端直接打資料庫」。傳統後端有 middleware 把關,Firebase 用什麼擋?答案是 **Security Rules**——一套部署在伺服器端、客戶端**完全無法竄改**的宣告式語言。 ### 基本語法範例 ```javascript service cloud.firestore { match /databases/{database}/documents { // 用戶只能讀寫自己的個人資料 match /users/{userId} { allow read, write: if request.auth != null && request.auth.uid == userId; } // 任何登入者都能讀貼文,但只有作者能改/刪 match /posts/{postId} { allow read: if request.auth != null; allow create: if request.auth != null && request.resource.data.authorId == request.auth.uid; allow update, delete: if request.auth != null && resource.data.authorId == request.auth.uid; } } } ``` ### 性能限制:那條 10 次 get() 的紅線 這是 Firebase 老手才知道的坑。根據 [官方規則條件文件](https://firebase.google.com/docs/firestore/security/rules-conditions) ,規則中跨文件查詢有嚴格上限: - **單一文件請求 / 查詢**:最多 **10 次** `get()` 或 `exists()` - **批次寫入 / 交易**:總計 **20 次**,但每個操作仍受 10 次限制 - 超過任一上限 → **permission denied error** 舉個實際情境: ```javascript // ❌ 容易踩到 10 次上限的寫法 match /comments/{commentId} { allow create: if get(/databases/$(database)/documents/posts/$(request.resource.data.postId)).data.authorId == request.auth.uid || get(/databases/$(database)/documents/admins/$(request.auth.uid)).data.role == "moderator" || get(/databases/$(database)/documents/teams/$(get(...).data.teamId)).data.members.hasAny([request.auth.uid]); } ``` 這種「rule 裡面跑 SQL」的寫法會立刻觸發上限。**正確解法是把權限資訊放進 [Custom Claims](https://firebase.google.com/docs/auth/admin/custom-claims) **: ```javascript // ✅ 直接讀 token 裡的 custom claim,零 get() 呼叫 match /comments/{commentId} { allow create: if request.auth.token.role in ["author", "moderator"]; } ``` 註冊用戶時透過 Admin SDK 設定 claim: ```typescript import { getAuth } from "firebase-admin/auth"; await getAuth().setCustomUserClaims(uid, { role: "moderator", teamId: "team-42", }); // 用戶下次 ID token 重新整理後就會帶上這些 claim ``` > **Tip**:每個 `get()`/`exists()` 都會被計算為一次 Firestore read,**會收費**——即使規則最終拒絕了請求。把權限放進 token 不只解決 10 次限制,也省下一筆讀取費用。 ### Security Rules 的其他冷知識限制 來自 [code.build 規則完整指南](https://code.build/p/firestore-security-rules-example-guide-eyfhvI) : - 函數最多 **7 個參數** - 最多 **10 個 `let` 變數** - 函數呼叫深度上限 **20** - 單次請求最多評估 **1000 個表達式** - **不支援 loop / recursion** 這些限制乍看很嚴苛,但其實是為了讓規則評估維持在納秒級。如果你發現規則寫不下了,那是訊號——你的資料模型該重新設計了。 --- ## 四、突破單一文件 1 寫/秒:分片計數器實戰 ![firebase-firestore-分片計數器](https://hackmd.io/_uploads/By1T_ZPAZx.jpg) Firestore 對單一文件的寫入頻率有個硬性限制:**約每秒 1 次**(受 [官方 best practices 文件](https://firebase.google.com/docs/firestore/best-practices) 證實)。對「按讚數」、「即時觀看人數」、「投票統計」這類場景,1 寫/秒根本不夠用。 解法是**分片計數器(Distributed Counters)**:把一個邏輯計數器拆成 N 個分片,寫入時隨機選一個。 ### 完整實作範例 以下程式碼基於 [oneuptime 2026 distributed counters 實作教學](https://oneuptime.com/blog/post/2026-02-17-how-to-implement-distributed-counters-in-firestore-for-high-write-scenarios/view) : ```javascript import { doc, setDoc, updateDoc, getDocs, collection, increment, writeBatch, getFirestore } from "firebase/firestore"; const db = getFirestore(); const NUM_SHARDS = 10; // 1️⃣ 初始化(只做一次) async function createCounter(counterPath) { const batch = writeBatch(db); batch.set(doc(db, counterPath), { numShards: NUM_SHARDS }); for (let i = 0; i < NUM_SHARDS; i++) { batch.set(doc(db, `${counterPath}/shards/${i}`), { count: 0 }); } return batch.commit(); } // 2️⃣ 寫入:隨機挑分片(每秒可達 NUM_SHARDS 次寫入) async function incrementCounter(counterPath, amount = 1) { const shardId = Math.floor(Math.random() * NUM_SHARDS); const shardRef = doc(db, `${counterPath}/shards/${shardId}`); await updateDoc(shardRef, { count: increment(amount) }); } // 3️⃣ 讀取:聚合所有分片 async function getCounterTotal(counterPath) { const shardsSnap = await getDocs(collection(db, `${counterPath}/shards`)); let total = 0; shardsSnap.forEach(snap => { total += snap.data().count; }); return total; } // 使用範例 await createCounter("counters/post-123-likes"); await incrementCounter("counters/post-123-likes"); const likes = await getCounterTotal("counters/post-123-likes"); ``` ### 寫入吞吐 vs 讀取成本的取捨 ```mermaid flowchart TB subgraph Write[寫入路徑] W1[Client 1] -->|increment| S0[Shard 0] W2[Client 2] -->|increment| S5[Shard 5] W3[Client 3] -->|increment| S9[Shard 9] end subgraph Read[讀取路徑] R[Client] -->|read all shards| Agg[Aggregate Sum] Agg --> Total[Total Count] end S0 -.summed.-> Agg S5 -.summed.-> Agg S9 -.summed.-> Agg ``` | 分片數 | 最大寫入吞吐 | 單次讀取成本 | | --- | --- | --- | | 10 | 10 寫/秒 | 10 reads | | 100 | 100 寫/秒 | 100 reads | | 1000 | 1000 寫/秒 | 1000 reads | 明顯地,分片越多寫得越快,但每讀一次總和就要付 N 次 read 的錢。對讀多寫多的場景(社群媒體按讚),可以加一個 **roll-up Cloud Function**,定期把所有分片加總寫到一個單一的「rollup 文件」: ```typescript import { onSchedule } from "firebase-functions/v2/scheduler"; import { getFirestore } from "firebase-admin/firestore"; // 每分鐘聚合一次 export const rollupCounters = onSchedule("every 1 minutes", async () => { const db = getFirestore(); const counterRef = db.doc("counters/post-123-likes"); const shards = await counterRef.collection("shards").get(); let total = 0; shards.forEach(doc => { total += doc.data().count; }); await counterRef.update({ rollupTotal: total, rollupAt: new Date() }); }); ``` 前端只讀 `counterRef` 一次(1 read)就拿到接近即時的總數。 --- ## 五、App Hosting vs Firebase Hosting:你該用哪個? ![firebase-app-hosting-vs-hosting](https://hackmd.io/_uploads/H1OT_ZD0bl.jpg) Firebase 在 2024 年中推出了 **App Hosting**,是給現代全端框架(Next.js、Angular、Astro)用的下一代託管方案。它和原本的 Firebase Hosting 差別很大,選錯會踩坑。 ### 差異全表 引用 [Firebase 官方部落格 App Hosting vs Hosting](https://firebase.blog/posts/2024/05/app-hosting-vs-hosting/) : | 維度 | Firebase Hosting(原版) | Firebase App Hosting(2024+) | | --- | --- | --- | | 適用場景 | 靜態網站 / SPA | 全棧 SSR / API routes | | Framework 感知 | 部分(透過 web frameworks 實驗) | **原生 framework-aware** | | SSR 後端 | Cloud Functions | **Cloud Run(直接)** | | 建置流程 | Firebase CLI | **Cloud Native Buildpacks** | | 動態擴展 | 透過 Functions | 原生 Cloud Run scale-to-zero | | GitHub 整合 | PR Preview Channels | 原生 Git push 部署 | ### 何時該用哪個? **用 Firebase Hosting(原版)的情況**: - 純前端 SPA(React、Vue、Svelte) - 靜態文件網站(部落格、Marketing site) - 需要極致 CDN 邊緣快取的場景 **用 App Hosting 的情況**: - Next.js 13+(App Router、Server Components) - Angular 17+ SSR - 需要 API routes / middleware 的應用 - 想要 GitHub push 自動部署的工作流 ### Hosting 整合 GitHub 的「Git push 即部署」 執行 `firebase init hosting:github` 後,CLI 會幫你做這幾件事: 1. 在 GCP 建立具部署權限的服務帳號 2. 加密 JSON 金鑰並上傳到 GitHub Secrets 3. 產生 YAML workflow,每個 PR 都會獲得**獨立預覽 URL** 4. PR 合併到 main 後自動上線 對小團隊來說,這幾乎就是免費的 CI/CD pipeline,比起自己刻 GitHub Actions + AWS S3 簡單太多。 --- ## 六、真實成本計算:Blaze、Supabase、VPS 三方對戰 ![firebase-成本對比-blaze-supabase-vps](https://hackmd.io/_uploads/HyxRd-vRWe.jpg) 成本是工程主管最關心的話題。我把 2026 年三方最新定價整理在一起,讓你看清楚不同規模下的真實差異。 ### Firebase Blaze 定價(2026/05) 引用自 [Firebase 官方定價頁](https://firebase.google.com/pricing) 與 [Tekpon 2026 計算彙整](https://tekpon.com/software/firebase/pricing/) : **Firestore(資料層)** | 項目 | Spark 免費 | Blaze 計費 | | --- | --- | --- | | Reads | 50K / 天 | $0.06 / 100K | | Writes | 20K / 天 | $0.18 / 100K | | Deletes | 20K / 天 | $0.02 / 100K | | 儲存 | 1 GB | $0.108–$0.026 / GB(階梯) | **Cloud Functions(運算層)** | 項目 | Spark 免費 | Blaze 計費 | | --- | --- | --- | | Invocations | 2M / 月 | $0.40 / 1M | | GB-seconds | 400K / 月 | 隨資源量計 | **Hosting / Storage(部署與儲存層)** | 項目 | Spark 免費 | Blaze 計費 | | --- | --- | --- | | Hosting 頻寬 | 10 GB / 月 | **$0.15 / GB(cached)、$0.20 / GB(uncached)** | | Storage 流量 | — | $0.15 / GB egress | | Auth phone SMS | — | $0.01–$0.06 / SMS | > **2026/02/03 重大變更**:[Firebase Cloud Storage 現在強制需要 Blaze 計畫](https://www.githubactionscost.online/supabase-vs-firebase-cost) ——即使你的用量在免費額度內。新專案如果只想用 Spark 計畫,得避開 Cloud Storage。 ### 三種規模情境模擬 **情境 A:MVP(1,000 DAU)** | 項目 | Firebase | Supabase | VPS(DigitalOcean) | | --- | --- | --- | --- | | 月費 | **$0**(Spark 涵蓋) | $0(Free tier) | $5–$20(固定) | | 維運人力 | $0 | $0 | 工程師 20% 工時 | | **小計** | **$0** | **$0** | **$5 + 人力** | **情境 B:成長期(10,000 DAU、50K API/天)** | 項目 | Firebase | Supabase | VPS | | --- | --- | --- | --- | | Firestore / DB | ~$30 | $0 | ~$10(VPS DB) | | Functions | ~$15 | $0 | $0 | | Hosting + Storage | ~$10 | $0 | $5 | | **小計** | **~$55** | **$25**(Pro 固定) | **~$25 + 人力** | **情境 C:規模化(10M DAU、read-heavy)** | 項目 | Firebase | Supabase | VPS | | --- | --- | --- | --- | | 資料庫 | $500–$1,500 | $200–$400 | 自管成本高 | | Functions / Edge | $300–$800 | 含於 Pro | 自管 | | **小計** | **$1,000–$3,000** | **$200–$600**(3–5 倍便宜) | **複雜** | 數據驗證:[Tech Insider 2026 對比](https://tech-insider.org/supabase-vs-firebase-2026-2/) 與 [Horizon Dev 客戶案例](https://horizon.dev/blog/supabase-vs-firebase-startups/) 都指出 read-heavy 場景下 Supabase 比 Firebase 便宜 3–5 倍。 ### 流量定價特別警告 如果你的應用是**影音串流**或**大量檔案下載**,Firebase Hosting 的 $0.20/GB egress 是個大坑。對比 [GPU Per Hour 2026 egress 對比](https://gpuperhour.com/reference/data-egress) : | 平台 | Egress 單價(per TB) | | --- | --- | | Cloudflare R2 | **$0**(!) | | Supabase | $90 | | Google Cloud | $120 | | Vercel | $150 | | **Firebase** | **$200** | | Netlify | $550 | 對流媒體類產品,把靜態大檔案放 Cloudflare R2 + Firebase 處理動態邏輯是常見的混合架構。 --- ## 七、生產踩坑:四個你絕對會遇到的問題 ![firebase-生產踩坑-警示](https://hackmd.io/_uploads/rJdROWPCbg.jpg) 讀完官方文件就能寫出生產級應用的時代結束了。我把這四年看過的真實踩坑案例整理成四類——每一條都是某個團隊用真金白銀換來的教訓。 ### 踩坑 1:把 SDK 滲透到業務邏輯(最深的鎖定) ```typescript // ❌ 業務邏輯直接呼叫 Firebase SDK async function getUser(userId: string) { const snap = await getDoc(doc(db, "users", userId)); return snap.data(); } ``` ```typescript // ✅ 透過 Repository pattern 抽象 interface UserRepository { findById(id: string): Promise<User | null>; } class FirestoreUserRepository implements UserRepository { async findById(id: string) { const snap = await getDoc(doc(db, "users", id)); return snap.exists() ? (snap.data() as User) : null; } } // 業務層只依賴介面 async function getUser(userId: string, repo: UserRepository) { return repo.findById(userId); } ``` 未來要遷到 Supabase / PostgreSQL 時,你只要新寫一個 `PostgresUserRepository`,業務層完全不動。 ### 踩坑 2:把 Firestore 當 SQL 用(N+1 帳單炸彈) ```typescript // ❌ 渲染一個貼文列表,每篇貼文額外查作者 → N+1 reads const posts = await getDocs(collection(db, "posts")); // 1 read × 100 = 100 reads for (const post of posts.docs) { const author = await getDoc(doc(db, "users", post.data().authorId)); // 100 reads } // 結果:渲染一個列表 = 200 reads,1000 個用戶看一次 = 200,000 reads ``` ```typescript // ✅ 去規範化:把作者基本資訊存進貼文 // 寫入時: await addDoc(collection(db, "posts"), { title: "...", content: "...", authorId: user.uid, authorName: user.displayName, // 冗餘 authorAvatar: user.photoURL, // 冗餘 }); // 讀取時:1 query 就拿到全部 const posts = await getDocs(collection(db, "posts")); // 100 reads only ``` 「寧可複製 100 次小資料,也不要查 100 次資料庫」——這是 Firestore 的鐵律。 ### 踩坑 3:全域初始化過載 → 部署 timeout ```typescript // ❌ 模組載入時就執行昂貴邏輯 import { GoogleAuth } from "google-auth-library"; const auth = new GoogleAuth({ /* ... */ }); const token = await auth.getAccessToken(); // 部署時就會跑 → timeout export const myFunction = onRequest((req, res) => { /* ... */ }); ``` ```typescript // ✅ 用 onInit hook 推遲到實例真正啟動時 import { onInit } from "firebase-functions/v2/core"; let auth: GoogleAuth; onInit(async () => { auth = new GoogleAuth({ /* ... */ }); }); export const myFunction = onRequest((req, res) => { // 在這裡 auth 已經初始化完成 }); ``` ### 踩坑 4:Security Rules 跨多文件 get() 觸發 10 次上限 承前面 Section 3 的範例,多層權限檢查很容易爆表。**永遠優先用 Custom Claims**,而不是寫巢狀 `get()`。 > **Q&A 補充**:規則 deploy 後生效要多久?根據 [zeriflow 安全最佳實踐](https://zeriflow.com/en/blog/firebase-security-best-practices) ,rules deployment 是原子操作,**幾秒鐘內生效**。但 client SDK 端可能因為網路快取延遲到 1 分鐘。生產部署時務必先在 Emulator Suite 跑完整測試。 --- ## 八、AI 時代的 Firebase:Genkit 與 Vertex AI 整合 ![firebase-genkit-ai-整合](https://hackmd.io/_uploads/SJAyt-vRWg.jpg) 2026 年的 Firebase 已經不只是 BaaS——它正快速成為 AI 應用的後端首選。從 [Firebase 在 Cloud Next 2026 的官方公告](https://firebase.blog/posts/2026/04/cloud-next-2026-announcements) 加上社群觀察,三個值得注意的動向: ### Genkit:開源 AI 應用框架 [Genkit](https://github.com/genkit-ai/genkit) 是 Google Firebase 團隊推出的開源 AI 框架,支援 JS / Go / Python,提供統一介面接 Google、OpenAI、Anthropic、Ollama 等模型。 ```typescript import { genkit, z } from "genkit"; import { googleAI } from "@genkit-ai/googleai"; const ai = genkit({ plugins: [googleAI()], model: "googleai/gemini-2.5-flash", }); export const summarize = ai.defineFlow( { name: "summarize", inputSchema: z.string(), outputSchema: z.string(), }, async (text) => { const { text: summary } = await ai.generate(`Summarize: ${text}`); return summary; } ); ``` flows 可以直接部署到 Cloud Functions / Cloud Run,所有 Telemetry 自動匯出到 Firebase Console。 ### Firebase AI Logic + Vertex AI [Firebase AI Logic](https://firebase.google.com/docs/ai-logic/models) 讓你直接從客戶端呼叫 Gemini 系列模型,免去自架 API Gateway。配合 App Check 確保只有你的 App 能呼叫。 最新支援的模型: - `gemini-2.5-flash`、`gemini-2.5-flash-lite`(生產推薦) - `imagen-4.0-generate-001`、`imagen-4.0-fast-generate-001`、`imagen-4.0-ultra-generate-001` - `gemini-live-2.5-flash-native-audio`(即時音訊) 注意 `gemini-2.0-flash` 系列將於 2026/06/01 停用,要遷到 2.5。 ### Google AI Studio + Firestore 全棧 = 自然語言寫 App Cloud Next 2026 最重磅的更新是:[Google AI Studio 現在直接整合 Firestore + Authentication](https://firebase.blog/posts/2026/04/cloud-next-2026-announcements) ,可以用自然語言生成包含後端的全棧應用。底層用 Cloud Run 跑 server code,Security Rules 也能自動草擬。 對小型專案而言,這幾乎是「Vibe Coding」的終極形式。 --- ## 九、決策清單:你該選 Firebase 還是 Supabase? 這個選擇沒有單一答案——同一家公司不同產品線可能要選不同方案。我把它拆成三個維度去問:你的產品在哪個階段?技術需求是什麼?規模有多大? ### 產品階段 | 階段 | 建議 | 理由 | | --- | --- | --- | | MVP / 概念驗證 | **Firebase** | Auth、即時同步、CDN 開箱即用,可用免費額度撐 1–3 個月 | | 已有 PMF、開始規模化 | 開始監控成本曲線 | 設定預算告警、檢視單頁 read 數 | | 大規模生產(>100K DAU) | **重新評估** | 視 read/write 比例、流量類型決定是否遷移 | ### 技術需求 | 需求 | 建議 | | --- | --- | | 重度 mobile(iOS/Android/Flutter) | **Firebase**(離線快取、FCM 推播無人能敵) | | 即時協作(聊天、Google Docs 級即時同步) | **Firebase**(Firestore listeners) | | 複雜報表、多表 JOIN、聚合查詢 | **Supabase / PostgreSQL** | | AI Agent 應用 | **Firebase + Genkit** | | 大檔案下載 / 流媒體 | 混合架構(**Firebase + Cloudflare R2**) | ### 抽象層保險策略 無論最後選誰,**在程式碼中保留抽象層**是必做的。 ```typescript // 把所有資料存取藏在 interface 後面 export interface DataStore { users: UserRepository; posts: PostRepository; // ... } // 啟動時注入具體實作 const store: DataStore = useFirebase ? new FirebaseDataStore(db) : new SupabaseDataStore(supabaseClient); ``` 短期多寫一兩百行,長期省下你半年重構時間。 --- ## 結語:不是不寫伺服器,是把伺服器寫進雲端基因 「無主機」聽起來像逃避,但它真正的意思是把伺服器的存在問題交給雲端、把心力放回業務邏輯上。 Firebase 在 2026 年做到的事情比 5 年前多太多了——2nd Gen Functions 解決了冷啟動、App Hosting 把 Next.js 部署簡化到極致、Genkit 讓 AI 應用三行 code 就能跑、AI Studio 開始能用自然語言寫全棧。 但你也看到了它的代價:Firestore 不是 SQL、Security Rules 有硬性上限、Egress 流量比同行貴一倍、Vendor Lock-in 風險真實存在。 回到開頭那家月帳單從 $1,200 飆到 $30,000 的 SaaS 公司——他們事後復盤發現,問題不是 Firebase「壞」,而是他們從第一天起就**沒有任何抽象層**,沒設預算告警,把 Firestore 當 PostgreSQL 用,列表頁觸發 N+1 reads。等到使用者破百萬,每多一個用戶就是一張帳單。 如果你正在開新專案,我會說:直接用 Firebase,但是**寫 Repository 介面、設 Budget Alert、用 Custom Claims**這三件事,請在第一週就做完。它們加起來不過半天工,能幫你避開 90% 的後悔。 無主機從來不是「免費的午餐」。它是一份你必須懂得怎麼吃的合約——讀懂它,你就能用一半的時間做出原本兩倍規模才做得出的產品。 --- ## 延伸閱讀 - [Firebase 官方文件 — Cloud Functions Version Comparison](https://firebase.google.com/docs/functions/version-comparison) - [Firebase Blog — App Hosting vs Hosting 哪一個適合你?](https://firebase.blog/posts/2024/05/app-hosting-vs-hosting/) - [Firebase 官方文件 — Firestore Security Rules Conditions](https://firebase.google.com/docs/firestore/security/rules-conditions) - [Firebase 官方文件 — Firestore Best Practices](https://firebase.google.com/docs/firestore/best-practices) - [Firebase Blog — Cloud Next 2026 公告](https://firebase.blog/posts/2026/04/cloud-next-2026-announcements) - [Genkit GitHub Repository](https://github.com/genkit-ai/genkit) - [Horizon Dev — Supabase vs Firebase 2026 startup 評測](https://horizon.dev/blog/supabase-vs-firebase-startups/) - [Tech Insider — Supabase vs Firebase 2026 三倍成本差距實測](https://tech-insider.org/supabase-vs-firebase-2026-2/) - [GPU Per Hour — 43+ 雲端供應商 Egress 定價總覽](https://gpuperhour.com/reference/data-egress) - [Java Code Geeks — Firebase Functions 冷啟動深度分析](https://www.javacodegeeks.com/2025/04/comprehensive-analysis-of-firebase-functions-cold-starts.html) --- **這篇對你有幫助嗎?** 如果你正在評估技術選型、或處理 Firebase 帳單失控的問題,歡迎留言分享你的踩坑經驗。下一篇我會寫「Firebase → Supabase 實戰遷移指南」,帶你看真實案例怎麼把 Firestore 結構翻譯成 PostgreSQL schema、Security Rules 改寫成 RLS。

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully