Rule Writing

CLAUDE.md for Firebase Projects

Firebase is a platform with security rules, not a database you query directly. AI skips security rules and mismodels Firestore data. Rules for NoSQL design, security, and Cloud Functions.

8 min read·April 24, 2025

No security rules = every client can read and write your entire database

Firestore document design, security rules, Firebase Auth, Cloud Functions, and modular SDK

Why Firebase Needs Security-First Rules

Firebase is a platform where the client talks directly to the database — there's no server in between for most operations. This means security rules are your ONLY protection against unauthorized access. AI assistants generate Firebase code without security rules: the database is wide open, every client can read and write everything. In production, this is a data breach waiting to happen.

The second major AI failure is data modeling: Firestore is a document database (not relational), and AI applies SQL normalization patterns that create excessive reads and poor performance. Firestore charges per document read — a normalized data model with 10 reads per page load costs 10x more than a denormalized model with 1 read.

These rules target Firebase v9+ (modular SDK) with Firestore, Auth, Cloud Functions, and Hosting. They enforce the security-first, cost-aware patterns that Firebase projects require.

Rule 1: Firestore Document Design

The rule: 'Design for your queries, not your entities. Firestore reads entire documents — if you only need a user's name, but the document contains 50 fields, you pay for 50 fields. Denormalize data that's read together: a post document contains the author's name and avatar, not just an authorId reference. Duplicate data is fine — storage is cheap, reads are expensive.'

For collections and subcollections: 'Use top-level collections for independently queryable data: /users, /posts, /orders. Use subcollections for data scoped to a parent: /users/{uid}/notifications, /orders/{orderId}/items. Subcollections enable scoped security rules and scoped queries. Never nest more than 2 levels deep — it complicates queries and rules.'

For query limitations: 'Firestore can't JOIN — every collection is queried independently. Firestore can't do inequality filters on multiple fields in a single query. Firestore requires composite indexes for multi-field queries — create them in firestore.indexes.json. Design your data model around the queries you need — not around entity relationships.'

  • Denormalize: duplicate data read together — storage is cheap, reads are expensive
  • Top-level collections for independent data — subcollections for scoped data
  • Max 2 levels of nesting — deeper complicates rules and queries
  • No JOINs — design for the queries you need, not entity relationships
  • Composite indexes in firestore.indexes.json for multi-field queries
💡 Reads Cost Money

Firestore charges per document read. A normalized model that reads 10 documents per page costs 10x more than a denormalized model that reads 1. Duplicate data that's read together — storage is cheap, reads are expensive.

Rule 2: Security Rules on Every Collection

The rule: 'Write Firestore security rules for every collection in firestore.rules. Default deny: rules_version = "2"; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if false; } } }. Then add specific allow rules per collection. Deploy with firebase deploy --only firestore:rules. Never use allow read, write: if true in production.'

For common patterns: 'User owns their data: allow read, write: if request.auth != null && request.auth.uid == resource.data.userId. Public read, auth write: allow read: if true; allow write: if request.auth != null. Validate data: allow create: if request.resource.data.title is string && request.resource.data.title.size() <= 200. Admin only: allow write: if request.auth.token.admin == true.'

AI generates no security rules — the default in development mode is allow all. In production, this means any user (or bot) can read, modify, or delete any document in your database. Security rules are the most critical Firebase configuration — they're your firewall.

⚠️ Default = Wide Open

Firebase development mode defaults to allow read, write: if true. In production, this means ANY client can read, modify, or delete ANY document. Deploy security rules before going live — they're your only firewall.

Rule 3: Firebase Auth Integration

The rule: 'Use Firebase Auth for all authentication: signInWithEmailAndPassword, signInWithPopup (Google, GitHub, etc.), createUserWithEmailAndPassword. Never build custom auth. Firebase Auth integrates with: Firestore security rules (request.auth.uid), Cloud Functions (context.auth), Storage rules (request.auth), and Analytics (user properties).'

For user profiles: 'Store additional user data in a /users/{uid} Firestore document — not in Auth's displayName/photoURL fields (limited). Create the profile document in a Cloud Function triggered by auth.user().onCreate(). This ensures every authenticated user has a profile, even if they signed up with OAuth.'

For custom claims: 'Use custom claims for role-based access: admin.auth().setCustomUserClaims(uid, { admin: true }). Access in security rules: request.auth.token.admin == true. Access in the client: (await user.getIdTokenResult()).claims.admin. Set claims in Cloud Functions — never from the client. Claims propagate on next token refresh (up to 1 hour).'

Rule 4: Cloud Functions Patterns

The rule: 'Use Cloud Functions for: background triggers (onDocumentCreated, onAuthCreate), scheduled tasks (onSchedule), HTTPS endpoints (onRequest, onCall), and any server-side logic that shouldn't run on the client. Use onCall for authenticated client-to-server calls — it automatically passes auth context. Use onRequest for webhooks and third-party integrations.'

For triggers: 'Use Firestore triggers for denormalization: when a user updates their name, update the name in all their posts. Use Auth triggers for user lifecycle: create a profile document on signup, clean up data on account deletion. Use Storage triggers for file processing: resize uploaded images, generate thumbnails.'

For best practices: 'Keep functions cold-start-friendly: minimize imports, lazy-load heavy modules. Use Firebase Admin SDK for server-side operations: admin.firestore(), admin.auth(), admin.storage(). Set region close to your users: functions.region("us-central1"). Use secrets for API keys: defineSecret("STRIPE_KEY").'

  • onDocumentCreated/Updated/Deleted for Firestore triggers
  • onCall for authenticated client calls — onRequest for webhooks
  • onSchedule for cron jobs — onAuthCreate for user lifecycle
  • Admin SDK for server-side ops — defineSecret for API keys
  • Minimize cold starts: lazy imports, small functions, regional deployment

Rule 5: Modular SDK and Cost Optimization

The rule: 'Use Firebase v9+ modular SDK: import { getFirestore, collection, getDocs } from "firebase/firestore". Never use the compat SDK (firebase.firestore()) — it doesn't tree-shake, shipping the entire Firebase library to the client. Modular imports reduce bundle size by 50-80%.'

For cost optimization: 'Firestore charges per read, write, and delete. Minimize reads: cache with onSnapshot listeners (only charged for initial read + changes, not repeated reads). Use select() to read specific fields from large documents. Batch writes: writeBatch for up to 500 operations in one request. Monitor usage in the Firebase console — set budget alerts.'

For offline: 'Enable Firestore offline persistence: enableIndexedDbPersistence(db). Reads are served from cache when offline. Writes are queued and synced when connectivity returns. Use onSnapshot for realtime listeners that work offline — getDocs does not. Offline support is free — no additional charges for cached reads.'

ℹ️ Modular = 50-80% Smaller

v9+ modular SDK (import { getDocs } from 'firebase/firestore') tree-shakes unused code. The compat SDK (firebase.firestore()) ships everything. One import style change cuts your Firebase bundle by 50-80%.

Complete Firebase Rules Template

Consolidated rules for Firebase projects.

  • Firestore: denormalize, design for queries — max 2 levels, composite indexes for multi-field
  • Security rules on EVERY collection — default deny, specific allows — never allow all
  • Firebase Auth: signIn/signUp/OAuth — custom claims for roles — never custom auth
  • Cloud Functions: triggers for denorm, onCall for auth client calls, onSchedule for cron
  • Modular SDK v9+: tree-shakeable imports — never compat firebase.firestore()
  • Cost: onSnapshot over getDocs (cached), select() for partial reads, writeBatch for bulk
  • Offline persistence with enableIndexedDbPersistence — free cached reads
  • firebase deploy for rules — firebase emulators for local testing — budget alerts