Skip to main content
Technology & EngineeringBaas261 lines

Firebase

Firebase BaaS with Firestore, Authentication, Cloud Functions, and Hosting

Quick Summary24 lines
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 lines
Paste into your CLAUDE.md or agent config

Firebase — 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 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.

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 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.

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

Get CLI access →