{"id":"firebase-cloud-firestore","name":"firebase-cloud-firestore","summary":"Firestoreの設定、スキーマ設計、CRUDの実行、リスナー作成、クエリのページ化、インデックスの設定、オフライン永続性の有効化、セキュリティルールの作成などに利用してください。","body":"# Firebase Cloud Firestore Skill\n\nThis skill defines how to correctly implement Cloud Firestore in Flutter applications, covering data modeling, queries, real-time updates, security rules, and scale optimization.\n\n## When to Use\n\nUse this skill when:\n\n* Setting up and configuring Cloud Firestore in a Flutter project.\n* Designing document and collection structure or planning subcollections.\n* Performing read, write, batch, or transaction operations.\n* Implementing real-time listeners or paginated queries.\n* Optimizing for scale and avoiding write hotspots.\n* Writing or debugging Firestore security rules.\n\n---\n\n## 1. Database Selection\n\nChoose **Cloud Firestore** when the app needs:\n- Rich, hierarchical data models with subcollections.\n- Complex queries: chaining filters, combining filtering and sorting on a property.\n- Transactions that atomically read and write data from any part of the database.\n- High availability (typical uptime 99.999%) or critical-level reliability.\n- Automatic scaling to millions of concurrent users.\n\nUse **Realtime Database** instead for simple data models requiring simple lookups and extremely low-latency synchronization (typical response times under 10ms).\n\n---\n\n## 2. Setup and Configuration\n\n```\nflutter pub add cloud_firestore\n```\n\n```dart\nimport 'package:cloud_firestore/cloud_firestore.dart';\n\nfinal db = FirebaseFirestore.instance; // after Firebase.initializeApp()\n```\n\n**Location:**\n- Select the database location closest to users and compute resources.\n- Use **multi-region** locations for critical apps (maximum availability and durability).\n- Use **regional** locations for lower costs and lower write latency.\n\n**iOS/macOS:** Consider pre-compiled frameworks to improve build times:\n```ruby\npod 'FirebaseFirestore',\n  :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git',\n  :tag => 'IOS_SDK_VERSION'\n```\n\n**Offline persistence** is enabled by default on mobile. Configure cache size:\n```dart\nFirebaseFirestore.instance.settings = const Settings(\n  persistenceEnabled: true,\n  cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED,\n);\n```\n\n---\n\n## 3. Document Structure\n\n- Avoid document IDs `.` and `..` (special meaning in Firestore paths).\n- Avoid forward slashes (`/`) in document IDs (path separators).\n- **Do not** use monotonically increasing document IDs (e.g., `Customer1`, `Customer2`) — causes write hotspots.\n- Use Firestore's **automatic document IDs** when possible:\n\n```dart\nfinal docRef = await db.collection(\"users\").add({\n  'name': 'Ada Lovelace',\n  'email': 'ada@example.com',\n  'created_at': FieldValue.serverTimestamp(),\n});\nprint('Created document with ID: ${docRef.id}');\n```\n\n- Avoid these characters in field names (require extra escaping): `.` `[` `]` `*` `` ` ``\n- Use **subcollections** within documents to organize complex, hierarchical data rather than deeply nested objects.\n\n---\n\n## 4. Indexing\n\n- Firestore queries are indexed by default; query performance is proportional to the result set size, not the dataset size.\n- Set **collection-level index exemptions** to reduce write latency and storage costs.\n- Disable Descending and Array indexing for fields that do not need them.\n- Exempt string fields with long values that are not used for querying.\n- Exempt fields with sequential values (e.g., timestamps) from indexing if not used in queries — avoids the 500 writes/second index limit.\n- Add single-field exemptions for TTL fields.\n- Exempt large array or map fields not used in queries — avoids the 40,000 index entries per document limit.\n\n---\n\n## 5. Read and Write Operations\n\n### Read All Documents in a Collection\n\n```dart\nfinal querySnapshot = await db.collection(\"users\").get();\nfor (var doc in querySnapshot.docs) {\n  print(\"${doc.id} => ${doc.data()}\");\n}\n```\n\n### Query with Filters\n\n```dart\nfinal query = db.collection(\"users\")\n    .where(\"age\", isGreaterThanOrEqualTo: 18)\n    .orderBy(\"age\")\n    .limit(20);\n\nfinal results = await query.get();\n```\n\n### Cursor-Based Pagination\n\n```dart\n// First page\nfinal first = db.collection(\"cities\").orderBy(\"name\").limit(25);\nfinal firstSnapshot = await first.get();\n\n// Next page using last document as cursor\nfinal lastDoc = firstSnapshot.docs.last;\nfinal next = db.collection(\"cities\")\n    .orderBy(\"name\")\n    .startAfterDocument(lastDoc)\n    .limit(25);\n```\n\n- **Do not use offsets for pagination** — use cursors to avoid retrieving and being billed for skipped documents.\n\n### Write with Server Timestamp\n\n```dart\nawait db.collection(\"users\").doc(\"user_1\").set({\n  'name': 'Grace Hopper',\n  'updated_at': FieldValue.serverTimestamp(),\n});\n```\n\n### Batch Write (Atomic, Up to 500 Operations)\n\n```dart\nfinal batch = db.batch();\nbatch.set(db.collection(\"cities\").doc(\"LA\"), {'name': 'Los Angeles'});\nbatch.update(db.collection(\"cities\").doc(\"SF\"), {'population': 860000});\nbatch.delete(db.collection(\"cities\").doc(\"OLD\"));\nawait batch.commit();\n```\n\n### Transaction\n\n```dart\nawait db.runTransaction((transaction) async {\n  final snapshot = await transaction.get(db.collection(\"counters\").doc(\"visits\"));\n  final currentCount = snapshot.get(\"count\") as int;\n  transaction.update(snapshot.reference, {\"count\": currentCount + 1});\n});\n```\n\n- Execute independent operations (e.g., a document lookup and a query) **in parallel**, not sequentially.\n- Be aware of write rate limits: ~1 write per second per document.\n- For writing a large number of documents, use a **bulk writer** instead of the atomic batch writer.\n\n---\n\n## 6. Designing for Scale\n\n- Avoid high read or write rates to **lexicographically close documents** (hotspotting).\n- Avoid creating new documents with **monotonically increasing fields** (like timestamps) at a very high rate.\n- Avoid **deleting documents** in a collection at a high rate.\n- **Gradually increase traffic** when writing to the database at a high rate — ramp up over 5 minutes.\n- Avoid queries that skip over recently deleted data — use `start_at` to find the correct start point.\n- Distribute writes across different document paths to avoid contention.\n- Firestore scales automatically to ~1 million concurrent connections and 10,000 writes/second.\n\n---\n\n## 7. Real-time Updates\n\n```dart\nfinal subscription = db.collection(\"messages\")\n    .where(\"room\", isEqualTo: \"general\")\n    .orderBy(\"timestamp\", descending: true)\n    .limit(50)\n    .snapshots()\n    .listen((querySnapshot) {\n      for (var change in querySnapshot.docChanges) {\n        switch (change.type) {\n          case DocumentChangeType.added:\n            print(\"New message: ${change.doc.data()}\");\n            break;\n          case DocumentChangeType.modified:\n            print(\"Modified: ${change.doc.data()}\");\n            break;\n          case DocumentChangeType.removed:\n            print(\"Removed: ${change.doc.id}\");\n            break;\n        }\n      }\n    });\n\n// Detach when no longer needed:\nsubscription.cancel();\n```\n\n- **Limit** the number of simultaneous real-time listeners.\n- **Detach listeners** when they are no longer needed to avoid memory leaks and unnecessary reads.\n- Use **compound queries** to filter data server-side rather than filtering on the client.\n- For large collections, use queries to limit the data being listened to — never listen to an entire large collection.\n\n---\n\n## 8. Security\n\n- Always use **Firebase Security Rules** to protect Firestore data.\n- Security rules **do not cascade** unless a wildcard is used.\n- If a query's results might contain data the user does not have access to, **the entire query fails**.\n\nExample rules for user-owned documents:\n\n```\nrules_version = '2';\nservice cloud.firestore {\n  match /databases/{database}/documents {\n    match /users/{userId} {\n      allow read, update, delete: if request.auth != null && request.auth.uid == userId;\n      allow create: if request.auth != null;\n    }\n  }\n}\n```\n\n- **Validate user input** before submitting to Firestore to prevent injection attacks.\n- Use **transactions** for operations that require atomic updates to multiple documents.\n- Implement proper **error handling** for all Firestore operations.\n- Never store sensitive information in Firestore without proper access controls.\n\n---\n\n## References\n\n- [Cloud Firestore Flutter documentation](https://firebase.google.com/docs/firestore/quickstart?hl=en&authuser=0&platform=flutter)\n- [Cloud Firestore best practices](https://firebase.google.com/docs/firestore/best-practices)\n- [Cloud Firestore security rules](https://firebase.google.com/docs/firestore/security/get-started)","author":"@evanca","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/evanca/flutter-ai-rules/tree/main/skills/firebase-cloud-firestore","license":"MIT","category":"writing","lang":"en","tokens":1876,"stars":0,"calls30d":1,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":["firebase.google.com"]}}