Firestore設計判断マトリクス:セキュリティ・パフォーマンス・コストを同時に満たす実践パターン
はじめに
Firestoreは「後から直すコストが高い」データベースです。
RDBMSであればテーブル構造を変更しても、ビューや正規化で吸収できる場合があります。しかしFirestoreでは、コレクション階層(パス設計)やドキュメント設計を誤ると、Security Rules・クエリ・インデックス・課金が絡んで“設計ごと移行”が必要になるケースが珍しくありません。
本記事では、セキュリティ・パフォーマンス・コストの3つの制約を同時に満たすFirestore設計パターンを、4つの代表パターンに整理して解説します。
この記事で学べること
本記事を読むことで、以下の知識が得られます。
Firestoreの3大制約(セキュリティ・パフォーマンス・コスト)とトレードオフ
Firestoreの設計では、以下の3つの制約が相互に影響します。
- セキュリティ: Security Rulesでのアクセス制御(パス基準 or ドキュメント内フィールド基準)
- パフォーマンス: コレクション階層とクエリ効率(collection group / インデックス含む)
- コスト: 読み取り・書き込み回数(+ストレージ、ネットワーク、クエリ種別による差分)
これらを個別に最適化しても、全体としては失敗します。本記事では、3つを同時に考える設計判断を解説します。
4つの代表的設計パターンと選択基準
以下の4パターンを、適用場面・Security Rules・クエリ例とともに提示します。
- ユーザー完全分離型(例:個人日記、タスク管理)
- 共有データ+参照型(例:SNS、コメント)
- サブコレクション+集約型(例:チャット、スレッド)
- ハイブリッド型(例:レポート生成、分析結果保存)
概算に基づく課金比較とパフォーマンス指標
「返ってくるドキュメント数=読み取り回数」を基本に、4パターンの読み取り回数・N+1・集約の効果を比較します。
よくあるアンチパターンと回避策
以下の3つのアンチパターンと、それを回避する方法を説明します。
- トップレベルに何でも置く(Rulesが複雑化し、クエリが通らなくなる)
- 1ドキュメントが肥大化(更新コストが増大、1MiB制限に抵触)
- 非正規化しすぎ(整合性と書き込みコストの悪化)
プロジェクト初期の設計チェックリスト
「後から変えにくい」設計を初手で決めるためのチェックリストを提供します。
想定読者
本記事は、以下のような読者を想定しています。
- Firebase Hostingでアプリを公開した経験がある
- Firestoreを使い始めたが、設計に悩んでいる
- RDBMSは分かるが、NoSQLの設計は不安
前提知識として、以下を仮定します。
- Firestoreの基本概念(コレクション、ドキュメント、クエリ)
- JavaScriptでのFirebase SDK操作(
getDoc,getDocs) - Security Rulesの雰囲気(
allow read: if ...など)
Firestoreの3大制約とトレードオフ
Firestoreの設計では、以下の3つの制約が常に付きまといます。
セキュリティ(Security Rules):ユーザー単位アクセス制御の原則
Firestoreでは、Security Rulesでアクセス制御を実装します。
まず、パス(コレクション階層)でユーザーを表現できる設計は、ルールがシンプルです。
// firestore.rules(抜粋)
// 例: users/{uid}/tasks/{taskId} にアクセスできるのは本人のみ
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function signedIn() { return request.auth != null; }
function isOwner(uid) { return signedIn() && request.auth.uid == uid; }
match /users/{uid}/tasks/{taskId} {
allow read: if isOwner(uid);
allow create, update, delete: if isOwner(uid);
}
}
}
一方、トップレベルに置く設計(tasks/{taskId})でも、ドキュメント内に所有者ID(例: userId)を持たせればルールは書けます。
// firestore.rules(抜粋)
// 例: tasks/{taskId} は userId フィールドで所有者を判定する
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function signedIn() { return request.auth != null; }
match /tasks/{taskId} {
allow read: if signedIn() && resource.data.userId == request.auth.uid;
allow create: if signedIn()
&& request.resource.data.userId == request.auth.uid;
// 所有者(userId)のすり替え防止
allow update: if signedIn()
&& resource.data.userId == request.auth.uid
&& request.resource.data.userId == resource.data.userId;
allow delete: if signedIn() && resource.data.userId == request.auth.uid;
}
}
}
ただし重要な注意点があります。
- Rulesはフィルタではありません(許可されないドキュメントを自動で除外してはくれません)
- クエリは 「返り得る結果セットがすべて許可される」場合のみ 成功します
→ つまりtasksを一覧するなら、クライアントはwhere('userId', '==', uid)など ルールと整合する条件を必ず付ける必要があります(条件が合わないとpermission-deniedで失敗)

図1: 4つの設計パターンの比較(セキュリティ・パフォーマンス・コストの3軸評価)
パフォーマンス(クエリとインデックス):コレクション階層の深さとクエリ可能性
Firestoreでは、サブコレクションは“その親パス配下”での検索が基本です。
例えば、以下の構造では「全ユーザーのタスクを横断して一覧」を取れません(=users/{uid} をまたげません)。
users/alice/tasks/task1
users/bob/tasks/task2
横断検索が必要なら、コレクショングループクエリを検討します。
// コレクショングループクエリ(全ユーザーの tasks を検索)
const q = query(collectionGroup(db, 'tasks'), where('status', '==', 'open'));
コレクショングループクエリ自体は便利ですが、次の点に注意が必要です。
- クエリ条件(
where/orderByの組み合わせ)によっては 複合インデックス(composite index)が必要になる
→ 実行時にエラーと作成リンクが出るので、それに従って作成するのが基本です - ルール設計によっては「必須フィルタ(例: ownerId)」が増え、クライアント実装が難しくなる
一方、トップレベルにコレクションを置くと、横断検索はシンプルです。
tasks/task1
tasks/task2
ただしこの場合、Security Rules(所有者判定)とクエリ条件(ownerId絞り込み) がセットになります。

図2: パターン別のコレクション階層(ユーザー分離型 vs 共有データ型 vs サブコレクション型)
コスト(読み取り・書き込み・ストレージ):ドキュメントアクセス回数を起点に考える
Firestoreの主要な課金要素は以下です。
- ドキュメントの読み取り / 書き込み / 削除
- ストレージ(データ+インデックス)
- ネットワーク(送受信)
- 一部クエリの追加課金(例: 集計クエリの index entry reads など)
本記事ではまず、「返ってきたドキュメント数 ≒ 読み取り回数」 を起点に概算します。
- 単価はリージョン等で変わります
→ 例として、Firebase公式の billing example に出てくる単価(reads:$0.06/100K, writes:$0.18/100K)を用いて計算します
つまり、以下のような設計判断が必要です。
- 非正規化してドキュメントを肥大化 → 1回の読み取りで全情報を取得(読み取り回数削減)
- 正規化してドキュメントを分割 → 参照解決で複数回の読み取りが必要(読み取り回数増加)
例えば、SNSのポスト一覧を表示する場合、以下の2つの設計があります。
設計A: 非正規化(ポストに投稿者情報を埋め込む)
posts/post1 = {
content: "Hello",
author: { uid: "alice", displayName: "Alice", photoURL: "..." }
}
- メリット: 1回の読み取りで表示可能
- デメリット: ユーザー情報が変わると全ポストを更新(書き込みが増える)
設計B: 正規化(ポストには投稿者IDだけ)
posts/post1 = {
content: "Hello",
authorId: "alice"
}
users/alice = {
displayName: "Alice",
photoURL: "..."
}
- メリット: ユーザー情報の更新コストが低い
- デメリット: ポスト一覧表示に複数回の読み取りが必要(読み取りが増える)

図3: 4パターンの概算モデル(100件・1000件・10000件の読み取り回数比較)
設計判断マトリクス(4つの代表パターン)
ここでは、4つの代表的設計パターンを提示します。
それぞれに、コレクション設計・Security Rules・クエリ例・適用場面をセットで説明します。
以降のSecurity Rulesは読みやすさのため抜粋しています。実際は
service cloud.firestore { match /databases/{database}/documents { ... } }の中に書きます。
パターン1: ユーザー完全分離型(例:個人日記、タスク管理)
コレクション設計
users/{uid}/tasks/{taskId}
users/{uid}/notes/{noteId}
- 全データを
users/{uid}/...配下に配置 - ユーザー間でデータを共有しない
Security Rules例
function signedIn() { return request.auth != null; }
function isOwner(uid) { return signedIn() && request.auth.uid == uid; }
match /users/{uid} {
// プロフィール等のユーザードキュメントも扱うなら、本人のみ許可
allow read, create, update, delete: if isOwner(uid);
match /tasks/{taskId} {
allow read: if isOwner(uid);
allow create, update, delete: if isOwner(uid);
}
match /notes/{noteId} {
allow read: if isOwner(uid);
allow create, update, delete: if isOwner(uid);
}
}
- パスだけで所有者が分かるためルールがシンプル
- 「本人の配下」を読むクエリは、基本的に
permission-deniedになりにくい
クエリ例(JavaScript SDK)
import { collection, query, where, getDocs } from 'firebase/firestore';
// 自分のタスクを取得
const tasksRef = collection(db, 'users', user.uid, 'tasks');
const q = query(tasksRef, where('status', '==', 'open'));
const snapshot = await getDocs(q);
snapshot.forEach(doc => {
console.log(doc.id, doc.data());
});
適用場面と制約
適用場面:
- 個人日記、タスク管理、メモアプリ
- ユーザー間でデータを共有しない
- 横断検索が不要
制約:
- 全ユーザーのタスクを一覧表示できない(必要ならコレクショングループクエリ)
- ユーザー間でのデータ共有が難しい(別設計が必要)
課金概算(単価例を用いた概算):
- 100タスクの一覧表示: 100回読み取り = $0.00006
- 1000タスクの一覧表示: 1000回読み取り = $0.0006
- 10000タスクの一覧表示: 10000回読み取り = $0.006
パターン2: 共有データ+参照型(例:SNS、コメント)
コレクション設計
posts/{postId} = {
content: "...",
authorId: "alice",
createdAt: Timestamp
}
users/{uid} = {
displayName: "Alice",
photoURL: "..."
}
- トップレベルに共有データ(
posts) - ユーザー情報は
users/{uid}で管理 - ポストには
authorIdだけを持たせる(参照)
Security Rules例
function signedIn() { return request.auth != null; }
match /posts/{postId} {
// 例: 公開ポストなら誰でも読める(機微情報を置かないこと)
allow read: if true;
// 作成は認証済みユーザーのみ
allow create: if signedIn()
&& request.resource.data.authorId == request.auth.uid;
// 更新は投稿者のみ(authorIdのすり替えも防ぐ)
allow update: if signedIn()
&& resource.data.authorId == request.auth.uid
&& request.resource.data.authorId == resource.data.authorId;
// 削除は投稿者のみ
allow delete: if signedIn() && resource.data.authorId == request.auth.uid;
}
match /users/{uid} {
// 例: 公開プロフィールなら誰でも読める(必要なら制限)
allow read: if true;
// 作成/更新/削除は本人のみ
allow create, update, delete: if signedIn() && request.auth.uid == uid;
}
クエリ例(JavaScript SDK)
import { collection, query, orderBy, limit, getDocs, getDoc, doc } from 'firebase/firestore';
// ポスト一覧を取得
const postsRef = collection(db, 'posts');
const q = query(postsRef, orderBy('createdAt', 'desc'), limit(20));
const snapshot = await getDocs(q);
// 各ポストの投稿者情報を取得(参照解決)
const postsWithAuthor = await Promise.all(
snapshot.docs.map(async (postDoc) => {
const postData = postDoc.data();
const authorSnap = await getDoc(doc(db, 'users', postData.authorId));
return {
id: postDoc.id,
...postData,
author: authorSnap.data(),
};
})
);
console.log(postsWithAuthor);
適用場面と制約
適用場面:
- SNS、ブログ、コメント機能
- ユーザー間でデータを共有する
- 横断検索が必要(全ポストを時系列表示など)
制約:
- ポスト一覧表示に N+1問題 が発生しやすい(20ポスト = 20回 + 最大20回のユーザー取得)
- ユーザー情報をキャッシュしないと課金が増える
→ 同一ユーザーが連続するケースは、メモ化やバッチ取得などで重複読み取りを減らす
課金概算(単価例を用いた概算):
- 20ポストの一覧表示: 20ポスト + 20ユーザー = 40回読み取り = $0.000024
- 100ポストの一覧表示: 100ポスト + 100ユーザー = 200回読み取り = $0.00012
- キャッシュあり(同一ユーザーの重複削減): 100ポスト + 50ユーザー = 150回読み取り = $0.00009
パターン3: サブコレクション+集約型(例:チャット、スレッド)
コレクション設計
threads/{threadId} = {
title: "...",
lastMessageAt: Timestamp,
messageCount: 100
}
threads/{threadId}/messages/{messageId} = {
content: "...",
senderId: "alice",
createdAt: Timestamp
}
users/{uid} = {
displayName: "Alice",
photoURL: "..."
}
- スレッド情報(
threads/{threadId})に 集約値 を持たせる - メッセージはサブコレクション(
threads/{threadId}/messages/{messageId})に格納
Security Rules例
function signedIn() { return request.auth != null; }
match /threads/{threadId} {
// 例: 公開スレッドなら誰でも読める(要件に応じて制限)
allow read: if true;
// 作成は認証済みユーザーのみ
allow create: if signedIn();
match /messages/{messageId} {
// 例: 公開スレッドのメッセージは読める
allow read: if true;
// 作成は認証済みユーザーのみ(送信者IDの偽装を防ぐ)
allow create: if signedIn()
&& request.resource.data.senderId == request.auth.uid;
// 編集/削除が必要なら update/delete を追加(要件次第)
}
}
クエリ例(JavaScript SDK)
import { collection, query, orderBy, limit, getDocs } from 'firebase/firestore';
// スレッド一覧を取得(集約値のみ)
const threadsRef = collection(db, 'threads');
const q = query(threadsRef, orderBy('lastMessageAt', 'desc'), limit(20));
const snapshot = await getDocs(q);
console.log('スレッド一覧:', snapshot.docs.map(doc => doc.data()));
// 特定スレッドのメッセージを取得
const threadId = 'thread1';
const messagesRef = collection(db, 'threads', threadId, 'messages');
const messagesQuery = query(messagesRef, orderBy('createdAt', 'asc'), limit(50));
const messagesSnapshot = await getDocs(messagesQuery);
console.log('メッセージ一覧:', messagesSnapshot.docs.map(doc => doc.data()));
適用場面と制約
適用場面:
- チャットアプリ、掲示板、コメントスレッド
- 1スレッドに大量のメッセージがある(100件以上)
- スレッド一覧には 最新メッセージ時刻や件数だけ を表示したい
制約:
- 集約値(
lastMessageAt,messageCount)の更新が必要
→ Cloud Functions / Cloud Run などで自動化推奨 - 全スレッドの全メッセージを横断検索できない
→ 必要ならコレクショングループクエリ(+インデックス設計)
課金概算(単価例を用いた概算):
- 20スレッドの一覧表示: 20回読み取り = $0.000012(メッセージは読まない)
- 1スレッドの50メッセージ表示: 50回読み取り = $0.00003
- 集約値の更新コスト(例): 1メッセージ追加 = 1書き込み(メッセージ) + 1書き込み(スレッド更新) = $0.0000036
パターン4: ハイブリッド型(例:レポート生成、分析結果保存)
コレクション設計
users/{uid}/rawData/{dataId} = {
timestamp: Timestamp,
value: 123
}
users/{uid}/reports/{reportId} = {
type: "monthly",
period: "2026-01",
summary: { total: 3000, average: 100 },
generatedAt: Timestamp
}
-
生データ(
rawData)と 集約データ(reports)を分離 - レポートはCloud FunctionsやCloud Runで定期生成
Security Rules例
function signedIn() { return request.auth != null; }
function isOwner(uid) { return signedIn() && request.auth.uid == uid; }
match /users/{uid} {
match /rawData/{dataId} {
allow read, create, update, delete: if isOwner(uid);
}
match /reports/{reportId} {
allow read: if isOwner(uid);
// 書き込みはAdmin SDKのみ(Cloud Functions / Cloud Run)
allow create, update, delete: if false;
}
}
クエリ例(JavaScript SDK)
import { collection, query, where, getDocs } from 'firebase/firestore';
// 生データを取得(通常は表示しない)
const rawDataRef = collection(db, 'users', user.uid, 'rawData');
const q = query(rawDataRef, where('timestamp', '>=', startDate));
const snapshot = await getDocs(q);
// レポートを取得(集約済み)
const reportsRef = collection(db, 'users', user.uid, 'reports');
const reportsQuery = query(reportsRef, where('type', '==', 'monthly'));
const reportsSnapshot = await getDocs(reportsQuery);
console.log('レポート一覧:', reportsSnapshot.docs.map(doc => doc.data()));
適用場面と制約
適用場面:
- IoTデータ、ログ、分析アプリ
- 大量の生データを持つが、ユーザーには 集約データだけ を見せる
- レポート生成はバックエンド(Cloud Functions / Cloud Run)で実行
制約:
- レポート生成のタイミングを制御する必要がある(リアルタイム性は低い)
- Cloud Functions / Cloud RunでAdmin SDKを使う必要がある(= IAMで守る)
課金概算(単価例を用いた概算):
- 月次レポート1件の表示: 1回読み取り = $0.0000006
- 生データ10000件からレポート生成: 10000回読み取り + 1回書き込み = $0.006 + $0.0000018 = $0.0060018
- ユーザー体験: 毎回10000件読むのではなく、1回のレポート生成で何度も参照できる

図4: コレクション階層とSecurity Rulesの対応関係(どのコレクションにどのルールが効くか)
よくあるアンチパターンと回避策
ここでは、よくある設計ミスとその回避策を説明します。
アンチパターン1: トップレベルに何でも置く
問題
以下のような設計をしてしまうと、Rulesとクエリ条件がセットになり、クライアント実装が難しくなります(特に一覧クエリ)。
// 例: 全ユーザーのタスクが tasks に混在
tasks/task1 = { userId: "alice", content: "..." }
tasks/task2 = { userId: "bob", content: "..." }
この構造でも、userId を持たせればルールは書けます。しかし、次の点が落とし穴です。
-
createではresourceが存在しないため、resource.dataではなくrequest.resource.dataが必要 - Rulesはフィルタではないため、クエリは必ず
where('userId','==',uid)のように“ルールと一致する条件”が必要
条件が足りないと 結果を返すのではなくクエリ自体が失敗します(permission-denied)
回避策
ユーザー単位で閉じるユースケース(タスク、メモ等)なら、最初から users/{uid}/... に分離するのが安全です。
users/alice/tasks/task1 = { content: "..." }
users/bob/tasks/task2 = { content: "..." }
Security Rulesもパスだけで判定でき、クエリも単純になります。
match /users/{uid}/tasks/{taskId} {
allow read, create, update, delete: if request.auth != null && request.auth.uid == uid;
}
アンチパターン2: 1ドキュメントが肥大化
問題
以下のような設計をしてしまうと、更新のたびに全体を読み書きすることになります。
// NG: 1ドキュメントに全メッセージを埋め込む
threads/thread1 = {
title: "...",
messages: [
{ senderId: "alice", content: "Hello" },
{ senderId: "bob", content: "Hi" },
// ... 1000件
]
}
Firestoreでは、ドキュメント(フィールド値)サイズに上限(1MiB) があります。また、配列に要素を追加するだけでもドキュメント全体の更新になり、競合や書き込みコストも増えがちです。
回避策
サブコレクションに分割します。
threads/thread1 = {
title: "...",
messageCount: 1000
}
threads/thread1/messages/msg1 = { senderId: "alice", content: "Hello" }
threads/thread1/messages/msg2 = { senderId: "bob", content: "Hi" }
これで、メッセージ追加は1ドキュメントの書き込みで済みます。
分割タイミングの判断基準(目安)
- 配列が10要素を超えるかもしれない → サブコレクションを検討
- 配列が100要素を超える見込み → サブコレクション推奨
- 要素が動的に増加する → 最初からサブコレクション推奨
アンチパターン3: 非正規化しすぎ
問題
以下のような設計をしてしまうと、更新箇所が増えて課金・整合性が崩れます。
// NG: 全ポストにユーザー情報を埋め込む
posts/post1 = {
content: "...",
author: { uid: "alice", displayName: "Alice", photoURL: "..." }
}
posts/post2 = {
content: "...",
author: { uid: "alice", displayName: "Alice", photoURL: "..." }
}
ユーザーが名前を変更すると、全ポストを更新する必要があります。
- ポストが1000件 = 1000回書き込み(単価例だと $0.0018 相当)
- 整合性の問題(更新漏れでデータが古いまま)
回避策
「どこまで非正規化するか」を判断します。
判断基準:
- 更新頻度: ほとんど変わらない情報は埋め込みやすい(例: displayName)
- 更新影響範囲: 更新時に影響するドキュメント数が小さいなら許容しやすい
- 表示頻度: 毎回表示するなら埋め込む価値がある
推奨設計:
// OK: 最低限の情報だけ埋め込む(displayNameのみ)
posts/post1 = {
content: "...",
authorId: "alice",
authorDisplayName: "Alice" // 更新頻度が低い想定
}
// 詳細情報は参照
users/alice = {
displayName: "Alice",
photoURL: "...",
bio: "..." // 変わる可能性がある
}
実装チェックリスト(プロジェクト初期に決める)
以下のチェックリストを使って、プロジェクト初期に設計を確定させます。
ユーザー単位 or 共有データの比率
-
データの大部分はユーザー単位か?(タスク、メモ、設定)
- → パターン1: ユーザー完全分離型
-
データの大部分は共有か?(SNSのポスト、コメント)
- → パターン2: 共有データ+参照型
Security Rulesの複雑度想定
-
パス(
users/{uid})だけで判定できるか?- → パターン1が有利
-
ドキュメント内のフィールド(
authorIdなど)を見る必要があるか?- → パターン2/4寄り(create/updateで
request.resource.dataの検証も必要)
- → パターン2/4寄り(create/updateで
-
複数条件の組み合わせ(
isPublic && (authorId == uid || isMember))が必要か?- → ルールのテストを徹底する(Emulator / Rules Playground)
クエリパターン(リスト表示・検索・集計)
-
全ユーザーのデータを横断検索する必要があるか?
- → トップレベル(パターン2)またはコレクショングループ(要件次第)
-
ユーザー単位でのみ検索するか?
- → サブコレクション(パターン1)
-
1スレッドに大量のサブデータがあるか?
- → サブコレクション+集約(パターン3)
想定トラフィック(読み取り回数の概算)
- 1日あたりの想定アクティブユーザー数は?
- 1ユーザーあたりの読み取り回数は?
- 月間読み取り回数 = アクティブユーザー × 読み取り回数 × 30日
- 課金概算 = 月間読み取り回数 × (read単価/100,000)
例(単価例: $0.06/100K reads):
- 1000 DAU × 100回/日 × 30日 = 3,000,000回/月 ≒ $1.8/月
インデックス戦略
-
複合クエリ(
where + orderBy、複数where)を使うか?- → 複合インデックスが必要になることが多い(実行時に作成リンクが出る)
-
コレクショングループクエリを使うか?
- → 条件次第で複合インデックスが必要。再現性のため
firestore.indexes.jsonで管理するのも有効
- → 条件次第で複合インデックスが必要。再現性のため
Admin SDKの使い所(運用注意)
-
レポート生成、集計処理があるか?
- → Cloud Functions / Cloud Runで実行(パターン4)
-
バッチ更新(全ドキュメントの一括更新)があるか?
- → Admin SDKで実行(Security Rulesをバイパス)
- → クライアントにサービスアカウント鍵を入れない / IAMとSecret管理で守る
まとめ
Firestoreの設計は、コレクション階層・Rules・クエリ条件・課金が密結合です。「後から変えればいい」は成立しにくいので、初手で設計判断を固めるのが重要です。
本記事では、以下を提供しました。
- 3つの制約(セキュリティ・パフォーマンス・コスト)を同時に考える視点
- 4つの代表的設計パターンと選択基準
- 概算モデルに基づく読み取り回数の比較
- よくあるアンチパターンと回避策
- プロジェクト初期に使える設計チェックリスト
パターン選択は、想定ユースケースから逆算します。
- 個人データ中心(タスク、メモ) → パターン1
- 共有データ中心(SNS、コメント) → パターン2
- 大量サブデータ(チャット、スレッド) → パターン3
- 生データ+集約(分析、レポート) → パターン4
次のステップ
本記事でFirestoreの設計判断を習得したら、次は以下のテーマに進むことをお勧めします。
Firebase Functions実践(集計処理、Webhook)
- Cloud Functionsで集約値を自動更新
- Firestore Triggersでスレッドの
messageCountを更新 - Webhookで外部APIと連携
Cloud Run × Firestore高度連携
- Admin SDKでバッチ処理
- Firestoreのバックアップ戦略
- Cloud Runからのストリーミング更新(Server-Sent Events)
Firestore × BigQuery連携(分析基盤)
- Firestoreのデータを自動でBigQueryにエクスポート
- SQLで集計・分析
- Data StudioやLooker Studioで可視化
Discussion