Firestore設計ベストプラクティス:UIDベース管理とその落とし穴

Firebase Firestore は、スケーラブルかつリアルタイムな NoSQL データベースとして、多くのモダンアプリで利用されています。特に Next.js や React、Vue.js などのモダンフロントエンドとの相性がよく、スタートアップや個人開発にも人気です。

本記事では、Firestore設計の中でも「UIDベースの管理」に焦点を当て、そのメリット・注意点・よくあるエラーについて解説します。特に Procom のようなユーザー中心のSNSサービスを開発している方にとって、有益な内容になっています。


UIDベースとは?

Firestore における UID(User ID)とは、Firebase Authentication などを使って取得される一意の識別子です。これをそのまま Firestore のドキュメントID として利用する設計を UIDベースと呼びます。

/users/{uid}/profile
/users/{uid}/settings
/users/{uid}/events

このように users コレクションに UIDをキーとするサブドキュメント・サブコレクションを持たせる構成です。


なぜ UIDベースが好まれるのか(メリット)

✅ データの一意性を担保できる

UIDは認証時に Firebase 側が一意に生成するため、ユーザー名の重複や衝突の心配がありません

✅ セキュリティルールをシンプルにできる

Firestoreのセキュリティルールでは、次のような形で「自分のUIDと一致しているドキュメントのみ許可」と記述できます:

match /users/{userId} {
  allow read, write: if request.auth != null && request.auth.uid == userId;
}

ユーザー単位で自動的にアクセス制限できるのが強みです。

✅ ドキュメントの取得が高速

ドキュメントIDに UID を使うことで、getDoc(doc(db, "users", uid)) のように一発で取得でき、インデックスも不要です。


UIDベース設計の注意点(落とし穴)

⚠️ 「username」をURLに使いたい場合に不便

URLを /user/uid12345 にしたくない!という場合:

/user/taro_yamada

のように「ユーザー名」をURLに使いたい開発者は多いです。しかし、UIDベースだとこのルーティングが複雑になります。

→ 解決策:Firestoreで username → uid のマッピングを別に持つ

/usernames/{username} → { uid: xxx }

⚠️ Cloud Functionsや複数コレクションとの整合性

たとえばユーザー退会時に、/users/{uid}/favorites/{uid}/comments/{uid} などを一括削除したいとき、コレクション分割の設計次第で難易度が上がります。

→ 解決策:削除時は Cloud Functions を使ってトリガー削除、または関連UIDドキュメントをまとめて設計する。


Firestore 設計のベストプラクティス

① データ構造はできるだけ浅く

Firestore は「深いネスト」より「平坦な構造」が高速に動作します。

NG:

/users/{uid}/settings/preferences/display/theme

OK:

/users/{uid}/settings → { theme: 'dark' }

② リストデータはサブコレクションで持つ

以下のような「予定一覧」などはサブコレクションが最適です。

/users/{uid}/events/{eventId} → { title, date }

→ 100件以上のリストでもスケーラブルです。

③ 更新頻度の高いデータは別コレクションで管理

プロフィール(滅多に更新しない)と「ログイン履歴」などの頻繁に書き換わるデータを同じドキュメントに入れると、読み取りコストやレイテンシが悪化します。

→ 分離しましょう。


よくあるエラーと対策

Missing or insufficient permissions エラー

原因:セキュリティルールの未設定 or 誤設定

  • Firestoreルールが request.auth.uid != resource.id になっていないか?
  • ローカル環境で firebase login を忘れていないか?

✅ 解決策:

allow read, write: if request.auth != null && request.auth.uid == userId;

Property contains an invalid nested entity エラー

原因:Firestore に空配列や undefined が送られている

✅ 対策:送信前に undefined や空オブジェクトを削除する

const cleanData = JSON.parse(JSON.stringify(data));

permission-denied or 403 本番のみ発生

原因:本番(Render/Vercel)で cookie やセッションが送られていない

✅ 対策:

  • Cookie Secure フラグを環境で切り替える
  • trust proxy を Express で設定する

まとめ

Firestore を UID ベースで設計することは、ユーザー管理が中心となるアプリにおいて非常に合理的なアプローチです。

ただし、その構成ゆえに URL設計や削除ロジックに注意が必要であり、サブコレクションの使い方やセキュリティルール設計も慎重に行う必要があります。

Procom のようなユーザー発信型プラットフォームを構築する際には、ぜひこの記事の内容を参考にして、安全かつ効率的なFirestore構築を目指してください。

おすすめの記事