| name | api-baas-firebase |
| description | Firebase backend-as-a-service — Firestore, Authentication, Cloud Functions v2, Storage, Hosting, Admin SDK, security rules, emulator suite |
Firebase Patterns
Quick Guide: Use Firebase as your backend-as-a-service for Firestore database, authentication, Cloud Functions, file storage, and hosting. Always use the modular SDK (firebase/app, firebase/firestore, etc.) for tree-shaking, type Firestore documents with TypeScript interfaces, write security rules for every collection, and use the Admin SDK only on the server.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)
(You MUST use the modular Firebase SDK imports (firebase/app, firebase/firestore, firebase/auth) -- NEVER use the deprecated firebase/compat namespace API)
(You MUST write Firestore security rules for EVERY collection -- a collection without rules is wide open in production)
(You MUST NEVER expose Firebase Admin SDK credentials or service account keys in client-side code)
(You MUST use Cloud Functions v2 API (firebase-functions/v2/https, firebase-functions/v2/firestore) -- NOT the deprecated v1 API)
(You MUST handle all Firestore operations with error checking -- never assume reads/writes succeed)
</critical_requirements>
Auto-detection: Firebase, initializeApp, firebase/app, firebase/firestore, firebase/auth, getFirestore, getAuth, onAuthStateChanged, collection, doc, getDocs, setDoc, updateDoc, deleteDoc, onSnapshot, firebase-admin, firebase-functions, Cloud Functions, Firestore security rules, firebase.json, firebase deploy
When to use:
- Initializing Firebase and configuring services (Firestore, Auth, Storage, Functions)
- Implementing authentication (email/password, OAuth, phone, custom tokens)
- Querying and writing Firestore documents (CRUD, real-time listeners, transactions)
- Writing Cloud Functions v2 (HTTP handlers, callable functions, Firestore triggers, scheduled functions)
- Uploading and serving files from Firebase Storage
- Deploying to Firebase Hosting with function rewrites
- Writing Firestore and Storage security rules
- Using the Firebase Admin SDK for server-side operations
Key patterns covered:
- Modular SDK setup with
initializeApp and service getters (getFirestore, getAuth, getStorage)
- Auth flows: sign up, sign in, OAuth, phone auth,
onAuthStateChanged, session management
- Firestore CRUD with
doc(), collection(), getDocs(), setDoc(), updateDoc(), deleteDoc()
- Firestore real-time listeners with
onSnapshot()
- Firestore queries with
where(), orderBy(), limit(), composite indexes
- Cloud Functions v2:
onRequest, onCall, onDocumentCreated, onSchedule
- Firebase Admin SDK:
initializeApp(), getFirestore(), getAuth(), custom tokens, user management
- Security rules: read/write granularity, auth-based access, data validation
- Emulator suite for local development and testing
- Offline persistence with
persistentLocalCache
When NOT to use:
- Complex relational queries needing JOIN operations (use a relational database with an ORM)
- Full server-side ORM patterns (Firestore is a document database, not relational)
- Applications using a non-Firebase authentication provider
- Applications requiring complex server-side business logic beyond Cloud Functions scope
Examples:
- Core Setup & Configuration -- App init, emulators, offline persistence
- Firestore Database -- CRUD, queries, real-time listeners, transactions
- Authentication -- Email/password, OAuth, auth state, profile sync
- Cloud Functions & Admin SDK -- HTTP, callable, triggers, scheduled, Admin SDK
- Cloud Storage -- Upload with progress, validation, App Check
- Security Rules -- Firestore and Storage rules patterns
Philosophy
Firebase is Google's Backend-as-a-Service platform providing a complete backend through Firestore (document database), Authentication, Cloud Functions, Storage, Hosting, and more. The modular SDK (v12+) uses tree-shakeable ES module imports for minimal bundle sizes.
Core principles:
- Modular imports for tree-shaking -- Import only what you need from
firebase/firestore, firebase/auth, etc. The modular SDK can reduce bundle size by 80%+ compared to the legacy namespace API.
- Document-oriented data model -- Firestore stores data as documents in collections. Design your data model around your query patterns, not normalized relations. Denormalization is expected.
- Security rules are mandatory -- Firestore and Storage are directly accessible from clients. Security rules are your only server-side access control. Every collection needs rules.
- Cloud Functions v2 on Cloud Run -- 2nd generation functions run on Cloud Run with better scaling, concurrency, longer timeouts (up to 60 minutes), and traffic splitting. Always use v2 for new projects.
- Offline-first with persistence -- Firestore supports offline persistence via IndexedDB. Enable it with
persistentLocalCache for apps that must work without connectivity.
- Admin SDK for server-side -- The Firebase Admin SDK bypasses security rules and has full access. Use it only in trusted server environments (Cloud Functions, API servers).
When to use Firebase:
- Rapid prototyping and MVPs with auth, database, and storage out of the box
- Real-time applications (chat, live dashboards, collaborative editing) via Firestore listeners
- Mobile and web apps needing offline support with automatic sync
- Projects wanting serverless backend logic with Cloud Functions
- Applications needing simple file storage with security rules
When NOT to use:
- Complex relational data with many-to-many relationships and JOINs (use a relational database)
- Applications needing full-text search (Firestore has limited query capabilities -- use a dedicated search service)
- High-write-throughput scenarios exceeding 10,000 writes/second to a single document
- Applications requiring complex server-side transactions spanning multiple services
Core Patterns
Pattern 1: Firebase App Initialization (Modular SDK)
Initialize Firebase with the modular SDK for tree-shaking. Each service has its own import path.
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { getAuth } from "firebase/auth";
import { getStorage } from "firebase/storage";
const app = initializeApp(firebaseConfig);
export const db = getFirestore(app);
export const auth = getAuth(app);
export const storage = getStorage(app);
Why good: Modular imports enable tree-shaking, each service initialized from the app instance, named exports
Full setup with emulators and offline persistence: examples/core.md
Pattern 2: Firestore CRUD Operations
Use modular functions for all Firestore read/write operations. Always type your documents.
const POSTS_COLLECTION = "posts";
const docSnap = await getDoc(doc(db, POSTS_COLLECTION, postId));
if (!docSnap.exists()) throw new Error(`Not found: ${postId}`);
const docRef = doc(collection(db, POSTS_COLLECTION));
await setDoc(docRef, { ...data, createdAt: serverTimestamp() });
await updateDoc(doc(db, POSTS_COLLECTION, postId), {
...data,
updatedAt: serverTimestamp(),
});
await deleteDoc(doc(db, POSTS_COLLECTION, postId));
Why good: Named constants for collection names, existence check before accessing data, serverTimestamp() for consistent timestamps, typed input parameters
Full CRUD service with pagination: examples/firestore.md
Pattern 3: Firestore Real-Time Listeners
Subscribe to document and collection changes with onSnapshot. Always unsubscribe to prevent memory leaks.
function subscribeToPublishedPosts(
onUpdate: (posts: Post[]) => void,
onError: (error: Error) => void,
): Unsubscribe {
const q = query(
collection(db, POSTS_COLLECTION),
where("published", "==", true),
orderBy("createdAt", "desc"),
);
return onSnapshot(
q,
(snapshot) => {
onUpdate(
snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }) as Post),
);
},
(error) => {
onError(new Error(`Listener failed: ${error.message}`));
},
);
}
const unsubscribe = subscribeToPublishedPosts(handler, errorHandler);
unsubscribe();
Why good: Returns Unsubscribe for cleanup, separate error callback, typed parameters
Real-time chat implementation: examples/firestore.md
Pattern 4: Firestore Transactions and Batched Writes
Use transactions for atomic read-then-write operations. Use batched writes for multiple writes without reads.
await runTransaction(db, async (transaction) => {
const postSnap = await transaction.get(postRef);
if (!postSnap.exists()) throw new Error("Not found");
transaction.update(postRef, { likeCount: increment(1) });
transaction.set(likeRef, { createdAt: serverTimestamp() });
});
const batch = writeBatch(db);
for (const id of postIds.slice(0, MAX_BATCH_SIZE)) {
batch.delete(doc(db, POSTS_COLLECTION, id));
}
await batch.commit();
Why good: Transaction ensures atomicity, increment() for safe counters, batch for bulk operations
Full examples: examples/firestore.md
Pattern 5: Authentication Flows
Use Firebase Authentication with the modular SDK. Handle auth state changes with onAuthStateChanged.
const credential = await createUserWithEmailAndPassword(auth, email, password);
const credential = await signInWithEmailAndPassword(auth, email, password);
await signOut(auth);
const result = await signInWithPopup(auth, new GoogleAuthProvider());
const unsubscribe = onAuthStateChanged(auth, (user) => {
});
const token = await auth.currentUser?.getIdToken( true);
Why good: Modular imports, typed User return values, Unsubscribe for cleanup
Full auth service with profile sync: examples/auth.md
Pattern 6: Cloud Functions v2
Write Cloud Functions using the v2 API. Import from firebase-functions/v2/* subpackages.
import { onRequest } from "firebase-functions/v2/https";
export const getPosts = onRequest(
{ cors: true, region: "us-central1" },
async (req, res) => {
},
);
import { onCall, HttpsError } from "firebase-functions/v2/https";
export const createPost = onCall({ region: "us-central1" }, async (request) => {
if (!request.auth)
throw new HttpsError("unauthenticated", "Must be signed in");
});
import { onDocumentCreated } from "firebase-functions/v2/firestore";
export const onPostCreated = onDocumentCreated(
{ document: "posts/{postId}", region: "us-central1" },
async (event) => {
},
);
import { onSchedule } from "firebase-functions/v2/scheduler";
export const cleanup = onSchedule(
{ schedule: "every 24 hours", region: "us-central1" },
async () => {
},
);
Why good: v2 imports from subpackages, region specified, HttpsError for callable error handling
Full Cloud Functions API: examples/functions.md
Pattern 7: Firebase Admin SDK (Server-Side)
Use the Admin SDK in Cloud Functions or trusted server environments. Bypasses all security rules.
import { initializeApp } from "firebase-admin/app";
import { getFirestore } from "firebase-admin/firestore";
import { getAuth } from "firebase-admin/auth";
initializeApp();
export const adminDb = getFirestore();
export const adminAuth = getAuth();
await adminAuth.setCustomUserClaims(uid, { admin: true });
const decoded = await adminAuth.verifyIdToken(idToken);
Why good: Modular Admin SDK imports, default credentials in Cloud Functions, custom claims for RBAC
Full Admin SDK patterns: examples/functions.md
Pattern 8: Firebase Storage
Upload, download, and manage files with Firebase Storage.
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
const storageRef = ref(storage, `avatars/${userId}/avatar.png`);
const uploadTask = uploadBytesResumable(storageRef, file, {
contentType: file.type,
customMetadata: { uploadedBy: userId },
});
uploadTask.on("state_changed", (snapshot) => {
const PERCENT_MULTIPLIER = 100;
const percent =
(snapshot.bytesTransferred / snapshot.totalBytes) * PERCENT_MULTIPLIER;
onProgress(percent);
});
Why good: Resumable upload with progress, customMetadata for audit trail, named constants
Full upload/download/delete with validation: examples/storage.md
Pattern 9: Security Rules
Firestore and Storage security rules are your primary access control mechanism.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function isAuthenticated() { return request.auth != null; }
function isOwner(userId) { return request.auth.uid == userId; }
match /posts/{postId} {
allow read: if resource.data.published == true
|| isOwner(resource.data.authorId);
allow create: if isAuthenticated()
&& request.resource.data.authorId == request.auth.uid;
allow update: if isOwner(resource.data.authorId);
allow delete: if isOwner(resource.data.authorId);
}
}
}
Why good: Helper functions, granular CRUD permissions, ownership checks, data validation
Full Firestore + Storage rules: examples/security-rules.md
Performance Optimization
Query Performance
- Use composite indexes -- Compound queries (multiple
where + orderBy) need composite indexes. Deploy via firestore.indexes.json or let Firestore error messages guide you with auto-generated index links.
- Limit result sets -- Always use
limit() to cap query results. Unbounded queries fetch all matching documents.
- Paginate with cursors -- Use
startAfter() with the last document snapshot for efficient pagination instead of offset().
- Select specific fields -- Use
select() in Admin SDK queries to fetch only needed fields (client SDK always fetches full documents).
Bundle Size
- Use
initializeAuth for granular control -- getAuth() enables all auth methods by default. Use initializeAuth() with only the providers you need for smaller bundles.
- Use
firebase/firestore/lite -- If you don't need real-time listeners, import from firebase/firestore/lite for a smaller Firestore bundle (no onSnapshot).
Cloud Functions
- Minimize cold starts -- Use
onInit() for lazy initialization. Keep function dependencies small. Consider "fat functions" (one entry point routing to handlers) over many small functions.
- Set appropriate memory/timeout -- Configure
memory and timeoutSeconds in function options instead of using defaults.
- Use global variables for reusable connections -- Initialize Admin SDK and database connections outside the function handler so they persist across invocations.
<decision_framework>
Decision Framework
Firestore vs Realtime Database
What does your app need?
+-- Complex queries (where, orderBy, compound) --> Firestore
+-- Simple key-value lookups with low latency --> Realtime Database
+-- Offline support with rich queries --> Firestore
+-- Presence system (online/offline status) --> Realtime Database
+-- Multi-region availability --> Firestore
+-- Very frequent small updates (typing indicators) --> Realtime Database
+-- For most new projects --> Firestore (recommended default)
Auth Method Selection
What auth flow does the user need?
+-- Email + Password --> createUserWithEmailAndPassword / signInWithEmailAndPassword
+-- Social login (Google, GitHub, etc.) --> signInWithPopup / signInWithRedirect
+-- Phone + SMS --> signInWithPhoneNumber (requires reCAPTCHA)
+-- Email link (passwordless) --> sendSignInLinkToEmail
+-- Custom backend auth --> Admin SDK createCustomToken + client signInWithCustomToken
+-- Anonymous (guest) --> signInAnonymously (upgrade later with linkWithCredential)
Cloud Functions: onRequest vs onCall
Who is calling the function?
+-- External webhooks, third-party services --> onRequest (raw HTTP)
+-- Your own client app (web/mobile) --> onCall (automatic auth, input validation)
+-- Firestore document changes --> onDocumentCreated / onDocumentUpdated / onDocumentDeleted
+-- Scheduled/cron tasks --> onSchedule
+-- Authentication events --> onUserCreated / onUserDeleted (from firebase-functions/v2/identity)
Client SDK vs Admin SDK
Where is the code running?
+-- Browser / Client-side --> Client SDK (firebase) -- security rules enforced
+-- Cloud Functions --> Admin SDK (firebase-admin) -- bypasses security rules
+-- API server / backend --> Admin SDK (firebase-admin) -- use service account
+-- NEVER use Admin SDK in client code --> It bypasses all security
Storage: When to Use Firebase Storage
What kind of files?
+-- User-generated content (avatars, uploads) --> Firebase Storage with security rules
+-- Public static assets (CSS, images) --> Firebase Hosting (faster CDN)
+-- Large files with progress tracking --> Firebase Storage with uploadBytesResumable
+-- Server-generated files (reports, exports) --> Firebase Storage via Admin SDK
</decision_framework>
Integration Guide
Firebase ecosystem integration:
- Firebase Hosting + Cloud Functions: Rewrite rules in
firebase.json route requests to functions
- App Check: Protect your backend resources from abuse by verifying requests come from your app (
initializeAppCheck with reCAPTCHA Enterprise)
- Firebase Extensions: Pre-built Cloud Functions for common tasks (payments, image resizing, email sending)
Client framework integration:
- Use
onAuthStateChanged in your framework's lifecycle (e.g., context provider, composable, store) to track auth state
- Firestore
onSnapshot listeners require cleanup when components unmount -- use your framework's cleanup mechanism
- Wrap Firestore reads in your data fetching solution's query functions for caching and real-time invalidation
Replaces / Conflicts with:
- Other BaaS platforms -- don't use two BaaS solutions for the same purpose
- Custom auth providers -- Firebase Auth can replace third-party auth, or vice versa -- don't mix auth providers
<red_flags>
RED FLAGS
High Priority Issues:
- Wide-open security rules in production -- The default test-mode rules (
allow read, write: if true) give everyone full access to your entire database. This is the single most common Firebase security vulnerability.
- Admin SDK credentials in client code -- Service account keys or Admin SDK imports in browser bundles give attackers full bypass of all security rules.
- Using the compat/namespace API --
firebase/compat/* imports prevent tree-shaking, bundling the entire SDK. The compat layer will be removed in a future major version.
- Not handling auth state changes -- Calling Firestore without waiting for
onAuthStateChanged can result in unauthenticated requests that fail silently against security rules.
Medium Priority Issues:
- Using v1 Cloud Functions API -- v1 (
functions.https.onRequest) lacks concurrency, traffic splitting, and longer timeouts. All new functions should use v2 (firebase-functions/v2/https).
- Not unsubscribing from
onSnapshot -- Leaked listeners keep WebSocket connections open, consume bandwidth, and cause memory leaks.
- Unbounded queries -- Queries without
limit() can fetch thousands of documents, causing performance issues and high read costs.
- Monotonically increasing document IDs -- Sequential IDs like
user1, user2 create hotspots. Use Firestore auto-generated IDs or UUIDs.
- Using
functions.config() -- Deprecated and will fail after March 2027. Use Cloud Secret Manager (defineSecret()) or environment variables.
Common Mistakes:
- Not adding
.select() after Admin SDK queries -- Without select(), all document fields are returned, increasing data transfer.
- Calling
getFirestore() on every operation -- Initialize once and reuse the instance. Each call creates overhead.
- Missing composite indexes -- Compound queries fail at runtime if the required composite index doesn't exist. Check error messages for the auto-generated index creation link.
- Deploying without testing security rules -- Use the emulator to test rules before deploying. Use
firebase emulators:exec "npm test" in CI.
- Using
enableIndexedDbPersistence -- Deprecated. Use initializeFirestore with persistentLocalCache instead.
Gotchas & Edge Cases:
- Firestore queries are "all or nothing" with security rules -- If a query could potentially return documents the user isn't allowed to read, the entire query fails (not just the unauthorized documents).
- Firestore limits -- 1 MB max document size, 20,000 fields per document, 500 writes per batch, 1 write per second per document sustained.
- Security rules propagation delay -- Rule updates take up to 1 minute to affect new queries, and up to 10 minutes for active listeners.
serverTimestamp() returns null in onSnapshot pending writes -- Until the server confirms the write, the timestamp field is null locally. Handle this with { serverTimestamps: 'estimate' } in snapshot options.
- Firebase Hosting has a 60-second timeout -- Even if your Cloud Function has a longer timeout, requests through Hosting rewrites timeout at 60 seconds.
onAuthStateChanged fires on page load -- It fires with null initially, then with the user if a session exists. Always handle the initial null state.
- Firestore
in queries are limited to 30 values -- where("field", "in", array) supports a maximum of 30 elements in the array (increased from 10 in recent versions).
- Cloud Functions cold starts -- First invocation after idle has additional latency. Use
onInit() for lazy initialization and keep dependencies minimal.
- Admin SDK
initializeApp() should be called once -- Multiple calls throw an error unless you provide a unique app name. Guard with a try-catch or check getApps().length.
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)
(You MUST use the modular Firebase SDK imports (firebase/app, firebase/firestore, firebase/auth) -- NEVER use the deprecated firebase/compat namespace API)
(You MUST write Firestore security rules for EVERY collection -- a collection without rules is wide open in production)
(You MUST NEVER expose Firebase Admin SDK credentials or service account keys in client-side code)
(You MUST use Cloud Functions v2 API (firebase-functions/v2/https, firebase-functions/v2/firestore) -- NOT the deprecated v1 API)
(You MUST handle all Firestore operations with error checking -- never assume reads/writes succeed)
Failure to follow these rules will create security vulnerabilities, bloated bundles, deprecated code paths, and silent runtime failures.
</critical_reminders>