dist/plugins/api-baas-firebase/skills/api-baas-firebase/SKILL.md
Firebase backend-as-a-service — Firestore, Authentication, Cloud Functions v2, Storage, Hosting, Admin SDK, security rules, emulator suite
npx skillsauth add agents-inc/skills api-baas-firebaseInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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>
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:
Key patterns covered:
initializeApp and service getters (getFirestore, getAuth, getStorage)onAuthStateChanged, session managementdoc(), collection(), getDocs(), setDoc(), updateDoc(), deleteDoc()onSnapshot()where(), orderBy(), limit(), composite indexesonRequest, onCall, onDocumentCreated, onScheduleinitializeApp(), getFirestore(), getAuth(), custom tokens, user managementpersistentLocalCacheWhen NOT to use:
Examples:
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:
firebase/firestore, firebase/auth, etc. The modular SDK can reduce bundle size by 80%+ compared to the legacy namespace API.persistentLocalCache for apps that must work without connectivity.When to use Firebase:
When NOT to use:
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
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
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
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
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
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
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
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
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
where + orderBy) need composite indexes. Deploy via firestore.indexes.json or let Firestore error messages guide you with auto-generated index links.limit() to cap query results. Unbounded queries fetch all matching documents.startAfter() with the last document snapshot for efficient pagination instead of offset().select() in Admin SDK queries to fetch only needed fields (client SDK always fetches full documents).initializeAuth for granular control -- getAuth() enables all auth methods by default. Use initializeAuth() with only the providers you need for smaller bundles.firebase/firestore/lite -- If you don't need real-time listeners, import from firebase/firestore/lite for a smaller Firestore bundle (no onSnapshot).onInit() for lazy initialization. Keep function dependencies small. Consider "fat functions" (one entry point routing to handlers) over many small functions.memory and timeoutSeconds in function options instead of using defaults.<decision_framework>
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)
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)
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)
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
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>
Firebase ecosystem integration:
firebase.json route requests to functionsinitializeAppCheck with reCAPTCHA Enterprise)Client framework integration:
onAuthStateChanged in your framework's lifecycle (e.g., context provider, composable, store) to track auth stateonSnapshot listeners require cleanup when components unmount -- use your framework's cleanup mechanismReplaces / Conflicts with:
<red_flags>
High Priority Issues:
allow read, write: if true) give everyone full access to your entire database. This is the single most common Firebase security vulnerability.firebase/compat/* imports prevent tree-shaking, bundling the entire SDK. The compat layer will be removed in a future major version.onAuthStateChanged can result in unauthenticated requests that fail silently against security rules.Medium Priority Issues:
functions.https.onRequest) lacks concurrency, traffic splitting, and longer timeouts. All new functions should use v2 (firebase-functions/v2/https).onSnapshot -- Leaked listeners keep WebSocket connections open, consume bandwidth, and cause memory leaks.limit() can fetch thousands of documents, causing performance issues and high read costs.user1, user2 create hotspots. Use Firestore auto-generated IDs or UUIDs.functions.config() -- Deprecated and will fail after March 2027. Use Cloud Secret Manager (defineSecret()) or environment variables.Common Mistakes:
.select() after Admin SDK queries -- Without select(), all document fields are returned, increasing data transfer.getFirestore() on every operation -- Initialize once and reuse the instance. Each call creates overhead.firebase emulators:exec "npm test" in CI.enableIndexedDbPersistence -- Deprecated. Use initializeFirestore with persistentLocalCache instead.Gotchas & Edge Cases:
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.onAuthStateChanged fires on page load -- It fires with null initially, then with the user if a session exists. Always handle the initial null state.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).onInit() for lazy initialization and keep dependencies minimal.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>
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>
development
Material Design component library for Vue 3
development
VitePress 1.x — Vue-powered static site generator for documentation sites, built on Vite
tools
Docusaurus 3.x documentation framework — site configuration, docs/blog plugins, sidebars, versioning, MDX, swizzling, and deployment
development
TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety