Firebase
Firebase BaaS with Firestore, Authentication, Cloud Functions, and Hosting
You are an expert in Firebase for rapid backend development, including Firestore, Authentication, Cloud Functions, Cloud Storage, and Firebase Hosting. ## Key Points - Use the Firestore emulator during development to avoid incurring costs and to test security rules locally. - Model data for the queries you need — denormalize aggressively in Firestore, since joins are not supported. - Use `serverTimestamp()` instead of `new Date()` to avoid clock-skew issues across clients. - Deploy security rules alongside code using `firebase deploy --only firestore:rules`. - Use batched writes or transactions when updating multiple documents atomically. - Prefer Cloud Functions v2 (2nd gen) for better scaling, concurrency control, and longer timeouts. - **No security rules**: By default, new Firestore databases start in test mode with open access — always deploy rules before going to production. - **Reading entire collections**: Firestore charges per document read; always use queries with filters and limits. - **Nested subcollections vs. root collections**: Subcollections cannot be queried across parents without collection group queries (`collectionGroup`). - **Cold starts in Cloud Functions**: First invocations take seconds; use `minInstances` in v2 functions for latency-sensitive endpoints. - **Not unsubscribing listeners**: Forgetting to call the `unsubscribe` function from `onSnapshot` causes memory leaks and unexpected reads. ## Quick Example ```bash # Deploy functions firebase deploy --only functions ```
skilldb get baas-skills/FirebaseFull skill: 261 linesFirebase — Backend as a Service
You are an expert in Firebase for rapid backend development, including Firestore, Authentication, Cloud Functions, Cloud Storage, and Firebase Hosting.
Core Philosophy
Overview
Firebase is Google's app development platform providing a suite of cloud services: Firestore (NoSQL document database), Firebase Authentication, Cloud Functions (serverless Node.js/Python), Cloud Storage for Firebase, Firebase Hosting (static + SSR), Remote Config, and Cloud Messaging. It integrates tightly with the Google Cloud Platform ecosystem.
Setup & Configuration
Project Initialization
# Install Firebase CLI
npm install -g firebase-tools
# Login
firebase login
# Initialize project (select Firestore, Functions, Hosting, etc.)
firebase init
# Start local emulators
firebase emulators:start
Client SDK Setup
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
import { getAuth } from 'firebase/auth';
import { getStorage } from 'firebase/storage';
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const auth = getAuth(app);
const storage = getStorage(app);
Admin SDK (Server-side)
import { initializeApp, cert } from 'firebase-admin/app';
import { getFirestore } from 'firebase-admin/firestore';
initializeApp({
credential: cert(JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT!)),
});
const adminDb = getFirestore();
Core Patterns
Authentication
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signInWithPopup,
GoogleAuthProvider,
onAuthStateChanged,
signOut,
} from 'firebase/auth';
// Sign up
const { user } = await createUserWithEmailAndPassword(auth, email, password);
// Sign in
const { user } = await signInWithEmailAndPassword(auth, email, password);
// Google OAuth
const provider = new GoogleAuthProvider();
const { user } = await signInWithPopup(auth, provider);
// Listen for auth state changes
onAuthStateChanged(auth, (user) => {
if (user) {
console.log('Signed in:', user.uid);
} else {
console.log('Signed out');
}
});
// Sign out
await signOut(auth);
Firestore Database
import {
collection, doc, addDoc, setDoc, getDoc, getDocs,
updateDoc, deleteDoc, query, where, orderBy, limit,
onSnapshot, serverTimestamp,
} from 'firebase/firestore';
// Add a document (auto-generated ID)
const docRef = await addDoc(collection(db, 'posts'), {
title: 'My Post',
body: 'Content here',
authorId: auth.currentUser.uid,
createdAt: serverTimestamp(),
});
// Set a document (explicit ID)
await setDoc(doc(db, 'users', userId), {
name: 'Alice',
email: 'alice@example.com',
}, { merge: true });
// Get a single document
const snap = await getDoc(doc(db, 'posts', postId));
if (snap.exists()) {
console.log(snap.data());
}
// Query documents
const q = query(
collection(db, 'posts'),
where('authorId', '==', userId),
orderBy('createdAt', 'desc'),
limit(10)
);
const snapshot = await getDocs(q);
snapshot.forEach((doc) => console.log(doc.id, doc.data()));
// Real-time listener
const unsubscribe = onSnapshot(q, (snapshot) => {
snapshot.docChanges().forEach((change) => {
if (change.type === 'added') console.log('New:', change.doc.data());
if (change.type === 'modified') console.log('Modified:', change.doc.data());
if (change.type === 'removed') console.log('Removed:', change.doc.data());
});
});
// Update
await updateDoc(doc(db, 'posts', postId), { title: 'Updated Title' });
// Delete
await deleteDoc(doc(db, 'posts', postId));
Firestore Security Rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Users can read/write their own profile
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
// Posts: anyone can read published, only author can write
match /posts/{postId} {
allow read: if resource.data.published == true;
allow create: if request.auth != null
&& request.resource.data.authorId == request.auth.uid;
allow update, delete: if request.auth != null
&& resource.data.authorId == request.auth.uid;
}
}
}
Cloud Storage
import { ref, uploadBytes, getDownloadURL, deleteObject } from 'firebase/storage';
// Upload
const storageRef = ref(storage, `avatars/${userId}/profile.jpg`);
const snapshot = await uploadBytes(storageRef, file);
const downloadURL = await getDownloadURL(snapshot.ref);
// Delete
await deleteObject(ref(storage, `avatars/${userId}/profile.jpg`));
Cloud Functions
// functions/src/index.ts
import { onRequest } from 'firebase-functions/v2/https';
import { onDocumentCreated } from 'firebase-functions/v2/firestore';
import { getFirestore } from 'firebase-admin/firestore';
import { initializeApp } from 'firebase-admin/app';
initializeApp();
const db = getFirestore();
// HTTP function
export const hello = onRequest(async (req, res) => {
res.json({ message: 'Hello from Firebase!' });
});
// Firestore trigger — runs when a new post is created
export const onPostCreated = onDocumentCreated('posts/{postId}', async (event) => {
const snap = event.data;
if (!snap) return;
const post = snap.data();
// Example: update author's post count
await db.doc(`users/${post.authorId}`).update({
postCount: FieldValue.increment(1),
});
});
# Deploy functions
firebase deploy --only functions
Best Practices
- Use the Firestore emulator during development to avoid incurring costs and to test security rules locally.
- Model data for the queries you need — denormalize aggressively in Firestore, since joins are not supported.
- Use
serverTimestamp()instead ofnew Date()to avoid clock-skew issues across clients. - Deploy security rules alongside code using
firebase deploy --only firestore:rules. - Use batched writes or transactions when updating multiple documents atomically.
- Prefer Cloud Functions v2 (2nd gen) for better scaling, concurrency control, and longer timeouts.
Common Pitfalls
- No security rules: By default, new Firestore databases start in test mode with open access — always deploy rules before going to production.
- Reading entire collections: Firestore charges per document read; always use queries with filters and limits.
- Nested subcollections vs. root collections: Subcollections cannot be queried across parents without collection group queries (
collectionGroup). - Cold starts in Cloud Functions: First invocations take seconds; use
minInstancesin v2 functions for latency-sensitive endpoints. - Not unsubscribing listeners: Forgetting to call the
unsubscribefunction fromonSnapshotcauses memory leaks and unexpected reads.
Anti-Patterns
Over-engineering for hypothetical requirements. Building for scenarios that may never materialize adds complexity without value. Solve the problem in front of you first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide wastes time and introduces risk.
Premature abstraction. Creating elaborate frameworks before having enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at system boundaries. Internal code can trust its inputs, but boundaries with external systems require defensive validation.
Skipping documentation. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add baas-skills
Related Skills
Appwrite
Appwrite self-hosted BaaS with database, auth, storage, and serverless functions
AWS Amplify
AWS Amplify BaaS with AppSync GraphQL, Cognito auth, S3 storage, and Lambda functions
Backendless
Backendless BaaS with real-time database, user authentication, Cloud Code,
Clerk Auth
Clerk authentication service with pre-built UI components, session management, and multi-framework support
Convex
Convex real-time backend with reactive queries, mutations, and serverless functions
Encore
Encore is a backend development platform that automatically provisions, configures, and manages cloud infrastructure based on your Go code. It simplifies building and deploying cloud-native applications by allowing you to focus purely on business logic.