Learn-skills.dev api-baas-firebase
Firebase backend-as-a-service — Firestore, Authentication, Cloud Functions v2, Storage, Hosting, Admin SDK, security rules, emulator suite
git clone https://github.com/NeverSight/learn-skills.dev
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/agents-inc/skills/api-baas-firebase" ~/.claude/skills/neversight-learn-skills-dev-api-baas-firebase && rm -rf "$T"
data/skills-md/agents-inc/skills/api-baas-firebase/SKILL.mdFirebase 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, 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.firebase/firestore
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
, named constants)import type
(You MUST use the modular Firebase SDK imports (
, firebase/app
, firebase/firestore
) -- NEVER use the deprecated firebase/auth
namespace API)firebase/compat
(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
) -- NOT the deprecated v1 API)firebase-functions/v2/firestore
(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
and service getters (initializeApp
,getFirestore
,getAuth
)getStorage - Auth flows: sign up, sign in, OAuth, phone auth,
, session managementonAuthStateChanged - Firestore CRUD with
,doc()
,collection()
,getDocs()
,setDoc()
,updateDoc()deleteDoc() - Firestore real-time listeners with
onSnapshot() - Firestore queries with
,where()
,orderBy()
, composite indexeslimit() - Cloud Functions v2:
,onRequest
,onCall
,onDocumentCreatedonSchedule - Firebase Admin SDK:
,initializeApp()
,getFirestore()
, custom tokens, user managementgetAuth() - 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>
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
, etc. The modular SDK can reduce bundle size by 80%+ compared to the legacy namespace API.firebase/auth - 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
for apps that must work without connectivity.persistentLocalCache - 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
<patterns>
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.
// lib/firebase.ts 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"; // Read const docSnap = await getDoc(doc(db, POSTS_COLLECTION, postId)); if (!docSnap.exists()) throw new Error(`Not found: ${postId}`); // Create with auto-ID and server timestamp const docRef = doc(collection(db, POSTS_COLLECTION)); await setDoc(docRef, { ...data, createdAt: serverTimestamp() }); // Update specific fields await updateDoc(doc(db, POSTS_COLLECTION, postId), { ...data, updatedAt: serverTimestamp(), }); // Delete 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}`)); }, ); } // Always store and call the unsubscribe function const unsubscribe = subscribeToPublishedPosts(handler, errorHandler); unsubscribe(); // Cleanup when done
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.
// Transaction: atomic read-then-write 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() }); }); // Batch: multiple writes (up to 500) 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.
// Sign up / Sign in / Sign out const credential = await createUserWithEmailAndPassword(auth, email, password); const credential = await signInWithEmailAndPassword(auth, email, password); await signOut(auth); // OAuth const result = await signInWithPopup(auth, new GoogleAuthProvider()); // Listen to auth state -- register early in app lifecycle const unsubscribe = onAuthStateChanged(auth, (user) => { /* ... */ }); // Get ID token for API calls const token = await auth.currentUser?.getIdToken(/* forceRefresh */ 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.
// HTTP function import { onRequest } from "firebase-functions/v2/https"; export const getPosts = onRequest( { cors: true, region: "us-central1" }, async (req, res) => { /* ... */ }, ); // Callable function (with auth) 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"); /* ... */ }); // Firestore trigger import { onDocumentCreated } from "firebase-functions/v2/firestore"; export const onPostCreated = onDocumentCreated( { document: "posts/{postId}", region: "us-central1" }, async (event) => { /* ... */ }, ); // Scheduled function 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(); // Default credentials in Cloud Functions export const adminDb = getFirestore(); export const adminAuth = getAuth(); // Custom claims for role-based access await adminAuth.setCustomUserClaims(uid, { admin: true }); // Verify client ID tokens 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
</patterns>Full Firestore + Storage rules: examples/security-rules.md
<performance>
Performance Optimization
Query Performance
- Use composite indexes -- Compound queries (multiple
+where
) need composite indexes. Deploy viaorderBy
or let Firestore error messages guide you with auto-generated index links.firestore.indexes.json - Limit result sets -- Always use
to cap query results. Unbounded queries fetch all matching documents.limit() - Paginate with cursors -- Use
with the last document snapshot for efficient pagination instead ofstartAfter()
.offset() - Select specific fields -- Use
in Admin SDK queries to fetch only needed fields (client SDK always fetches full documents).select()
Bundle Size
- Use
for granular control --initializeAuth
enables all auth methods by default. UsegetAuth()
with only the providers you need for smaller bundles.initializeAuth() - Use
-- If you don't need real-time listeners, import fromfirebase/firestore/lite
for a smaller Firestore bundle (nofirebase/firestore/lite
).onSnapshot
Cloud Functions
- Minimize cold starts -- Use
for lazy initialization. Keep function dependencies small. Consider "fat functions" (one entry point routing to handlers) over many small functions.onInit() - Set appropriate memory/timeout -- Configure
andmemory
in function options instead of using defaults.timeoutSeconds - 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>
Integration Guide
Firebase ecosystem integration:
- Firebase Hosting + Cloud Functions: Rewrite rules in
route requests to functionsfirebase.json - App Check: Protect your backend resources from abuse by verifying requests come from your app (
with reCAPTCHA Enterprise)initializeAppCheck - Firebase Extensions: Pre-built Cloud Functions for common tasks (payments, image resizing, email sending)
Client framework integration:
- Use
in your framework's lifecycle (e.g., context provider, composable, store) to track auth stateonAuthStateChanged - Firestore
listeners require cleanup when components unmount -- use your framework's cleanup mechanismonSnapshot - 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 (
) give everyone full access to your entire database. This is the single most common Firebase security vulnerability.allow read, write: if true - 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 --
imports prevent tree-shaking, bundling the entire SDK. The compat layer will be removed in a future major version.firebase/compat/* - Not handling auth state changes -- Calling Firestore without waiting for
can result in unauthenticated requests that fail silently against security rules.onAuthStateChanged
Medium Priority Issues:
- Using v1 Cloud Functions API -- v1 (
) lacks concurrency, traffic splitting, and longer timeouts. All new functions should use v2 (functions.https.onRequest
).firebase-functions/v2/https - Not unsubscribing from
-- Leaked listeners keep WebSocket connections open, consume bandwidth, and cause memory leaks.onSnapshot - Unbounded queries -- Queries without
can fetch thousands of documents, causing performance issues and high read costs.limit() - Monotonically increasing document IDs -- Sequential IDs like
,user1
create hotspots. Use Firestore auto-generated IDs or UUIDs.user2 - Using
-- Deprecated and will fail after March 2027. Use Cloud Secret Manager (functions.config()
) or environment variables.defineSecret()
Common Mistakes:
- Not adding
after Admin SDK queries -- Without.select()
, all document fields are returned, increasing data transfer.select() - Calling
on every operation -- Initialize once and reuse the instance. Each call creates overhead.getFirestore() - 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
in CI.firebase emulators:exec "npm test" - Using
-- Deprecated. UseenableIndexedDbPersistence
withinitializeFirestore
instead.persistentLocalCache
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.
returnsserverTimestamp()
innull
pending writes -- Until the server confirms the write, the timestamp field isonSnapshot
locally. Handle this withnull
in snapshot options.{ serverTimestamps: 'estimate' }- Firebase Hosting has a 60-second timeout -- Even if your Cloud Function has a longer timeout, requests through Hosting rewrites timeout at 60 seconds.
fires on page load -- It fires withonAuthStateChanged
initially, then with the user if a session exists. Always handle the initialnull
state.null- Firestore
queries are limited to 30 values --in
supports a maximum of 30 elements in the array (increased from 10 in recent versions).where("field", "in", array) - Cloud Functions cold starts -- First invocation after idle has additional latency. Use
for lazy initialization and keep dependencies minimal.onInit() - Admin SDK
should be called once -- Multiple calls throw an error unless you provide a unique app name. Guard with a try-catch or checkinitializeApp()
.getApps().length
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
, named constants)import type
(You MUST use the modular Firebase SDK imports (
, firebase/app
, firebase/firestore
) -- NEVER use the deprecated firebase/auth
namespace API)firebase/compat
(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
) -- NOT the deprecated v1 API)firebase-functions/v2/firestore
(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>