Skip to content
Development
Skill

/firebase

Firebase Firestore, Auth, Storage, real-time listeners, security rules

From plugin
maggy
71073 skills1 agent25 commands1 hook

Claude Code

$ npx -y skills add alinaqi/maggy --skill firebase --agent claude-code

SKILL.md

firebase.SKILL.md
name: firebase
description: Firebase Firestore, Auth, Storage, real-time listeners, security rules
when-to-use: When working with Firebase services
user-invocable: false
paths: ["**/firebase*", "firestore.rules", "storage.rules", "firebase.json"]
effort: medium

Firebase Skill

Firebase/Firestore patterns for web and mobile applications with real-time data, offline support, and security rules.

**Sources:** [Firebase Docs](https://firebase.google.com/docs) | [Firestore Best Practices](https://firebase.google.com/docs/firestore/best-practices) | [Security Rules](https://firebase.google.com/docs/rules)

---

Core Principle

**Denormalize with purpose, secure with rules, scale horizontally.**

Firestore is a document database - embrace denormalization for read efficiency. Security rules are your server-side validation. Design for your access patterns.

---

Firebase Stack

| Service | Purpose | |---------|---------| | **Firestore** | NoSQL document database with real-time sync | | **Authentication** | User auth, OAuth, anonymous sessions | | **Storage** | File uploads with security rules | | **Functions** | Serverless backend (Node.js) | | **Hosting** | Static site + CDN | | **Extensions** | Pre-built solutions (Stripe, Algolia, etc.) |

---

Project Setup

Install Firebase CLI

# Install globally
npm install -g firebase-tools

# Login
firebase login

# Initialize in project
firebase init

Initialize with Emulators

firebase init emulators

# Start local development
firebase emulators:start

Project Structure

project/
├── firebase.json           # Firebase config
├── firestore.rules         # Security rules
├── firestore.indexes.json  # Composite indexes
├── storage.rules           # Storage security rules
└── functions/              # Cloud Functions
    ├── src/
    ├── package.json
    └── tsconfig.json

---

Firestore Data Modeling

Document Structure

// Good: Flat documents with all needed data
interface Post {
  id: string;
  title: string;
  content: string;
  authorId: string;
  authorName: string;      // Denormalized for display
  authorAvatar: string;    // Denormalized
  tags: string[];
  likeCount: number;       // Aggregated counter
  createdAt: Timestamp;
  updatedAt: Timestamp;
}

// Collection: posts/{postId}

When to Use Subcollections

// Use subcollections for:
// 1. Unbounded lists (comments, messages)
// 2. Data with different access patterns
// 3. Data that grows independently

// posts/{postId}/comments/{commentId}
interface Comment {
  id: string;
  text: string;
  authorId: string;
  authorName: string;
  createdAt: Timestamp;
}

Data Model Patterns

// Pattern 1: Embedded data (bounded, always needed)
interface User {
  id: string;
  email: string;
  profile: {
    displayName: string;
    bio: string;
    avatar: string;
  };
  settings: {
    notifications: boolean;
    theme: 'light' | 'dark';
  };
}

// Pattern 2: Reference with denormalization
interface Order {
  id: string;
  userId: string;
  userEmail: string;  // Denormalized for display
  items: OrderItem[]; // Embedded (bounded)
  total: number;
  status: 'pending' | 'paid' | 'shipped';
}

// Pattern 3: Aggregation documents
// Keep counters in parent document
interface Channel {
  id: string;
  name: string;
  memberCount: number;  // Updated via Cloud Function
  messageCount: number;
}

---

TypeScript SDK (Modular v9+)

Initialize Firebase

// lib/firebase.ts
import { initializeApp, getApps } from 'firebase/app';
import { getFirestore, connectFirestoreEmulator } from 'firebase/firestore';
import { getAuth, connectAuthEmulator } from 'firebase/auth';
import { getStorage, connectStorageEmulator } 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
};

// Initialize only once
const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];

export const db = getFirestore(app);
export const auth = getAuth(app);
export const storage = getStorage(app);

// Connect to emulators in development
if (process.env.NODE_ENV === 'development') {
  connectFirestoreEmulator(db, 'localhost', 8080);
  connectAuthEmulator(auth, 'http://localhost:9099');
  connectStorageEmulator(storage, 'localhost', 9199);
}

CRUD Operations

import {
  collection,
  doc,
  getDoc,
  getDocs,
  addDoc,
  setDoc,
  updateDoc,
  deleteDoc,
  query,
  where,
  orderBy,
  limit,
  startAfter,
  serverTimestamp,
  Timestamp
} from 'firebase/firestore';
import { db } from './firebase';

// Create
async function createPost(data: Omit<Post, 'id' | 'createdAt' | 'updatedAt'>) {
  const docRef = await addDoc(collection(db, 'posts'), {
    ...data,
    createdAt: serverTimestamp(),
    updatedAt: serverTimestamp()
  });
  return docRef.id;
}

// Read single document
async function getPost(postId: string): Promise<Post | null> {
  const docSnap = await getDoc(doc(db, 'posts', postId));
  if (!docSnap.exists()) return null;
  return { id: docSnap.id, ...docSnap.data() } as Post;
}

// Query with filters
async function getPostsByAuthor(authorId: string, pageSize = 10) {
  const q = query(
    collection(db, 'posts'),
    where('authorId', '==', authorId),
    orderBy('createdAt', 'desc'),
    limit(pageSize)
  );
  const snapshot = await getDocs(q);
  return snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() } as Post));
}

// Pagination
async function getNextPage(lastDoc: Post, pageSize = 10) {
  const q = query(
    collection(db, 'posts'),
    orderBy('createdAt', 'desc'),
    startAfter(lastDoc.createdAt),
Read more
@alinaqi
Ships withmaggy

Turn Claude Code into a self-reviewing, test-enforced engineering system that remembers context across sessions — then route work across 13 models from a single dashboard.

71073 skills1 agent
Get the whole plugin

Stars on GitHub

Momentum

stars in total
710
Star history of alinaqi/maggy on GitHub, a chart by star-history.com

Other skills on maggy.

aeo-optimization

aeo-optimization

AI Engine Optimization - semantic triples, page templates, content clusters for AI citations

@alinaqi@alinaqiInstall Skill
agent-teams

agent-teams

Claude Code Agent Teams - default team-based development with strict TDD pipeline enforcement

@alinaqi@alinaqiInstall Skill
ai-models

ai-models

Latest AI models reference - Claude, OpenAI, Gemini, Eleven Labs, Replicate

@alinaqi@alinaqiInstall Skill
android-kotlin

android-kotlin

Android Kotlin development with Coroutines, Jetpack Compose, Hilt, and MockK testing

@alinaqi@alinaqiInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills12d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react3d252k
@facebook@facebookInstall Skill

Questions about firebase.

Which coding agents can use firebase?
firebase installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.