界面已本地化;技能介绍与说明文档按所选语言由 AI 自动翻译,代码、命令、路径和专业术语保留原文,以原作者文档为准。
探索技能
设计与创意 · SKILL.md

firebase

firebase

【设计与创意】Use this skill when working with Firebase platform — Firestore, Authentication, Cloud Storage, Cloud Functions, Hosting, Security Rules, Firebase Admin SDK, Firebase Extensions. This skill enforces: proper security rules, Firestore data modeling (no nesting >3), auth provider configuration, function cold start mitigation, cost-aware query design. Do …

独立 GitHub 快照 · 自动分类 · 以原作者文档为准

技能说明

GitHub

name: firebase description: > Use this skill when working with Firebase platform — Firestore, Authentication, Cloud Storage, Cloud Functions, Hosting, Security Rules, Firebase Admin SDK, Firebase Extensions. This skill enforces: proper security rules, Firestore data modeling (no nesting >3), auth provider configuration, function cold start mitigation, cost-aware query design. Do NOT use for: Supabase, AWS Amplify, custom backend, general PostgreSQL/NoSQL database design. version: "2.0.0" author: "j4flmao" license: "MIT" compatibility: claude-code: true cursor: true codex: true windsurf: true tags: [backend, universal, firebase, baas, phase-4]

Firebase

Purpose

Architect serverless backends on Firebase — Firestore document modeling, Authentication providers, Cloud Functions triggers, Storage security, Hosting configuration, and operational best practices.

Agent Protocol

Trigger

User request includes: Firebase, Firestore, Firebase Auth, Cloud Functions, Firebase Storage, Firebase Hosting, security rules, Firebase Admin, Firebase Extensions, Firebase App Check, Firebase Remote Config, Firebase emulator, Firebase cost.

Input Context

  • All Firebase capabilities needed (DB, auth, storage, functions, hosting)
  • Target platforms (web, iOS, Android, Node.js admin)
  • Estimated user/request scale
  • Auth providers (email, Google, Apple, custom)

Output Artifact

Firebase project architecture, security rules, data model, function triggers, hosting config.

Response Format

Produce artifact directly. No preamble, no postamble, no explanations. No filler, no hedging, no transitions. Strip articles a/an/the where unambiguous. Compress output — why use many token when few do trick.

Completion Criteria

  • Firestore data model designed (collections, documents, indexes, subcollections)
  • Security rules defined (read/write/validate per collection)
  • Auth providers configured (web + mobile SDK setup)
  • Cloud Functions triggers mapped (onCreate, onUpdate, onDelete, onRequest, scheduled)
  • Storage security rules + Hosting config (rewrites, headers, redirects)
  • Cost optimization plan (read/write counts, indexes, function timeout/memory)

Max Response Length

4096 tokens

Decision Tree

Firestore vs Realtime Database?

What data access pattern do you need?
  ├── Complex queries, filtering, sorting, transactions
  │   └── Firestore — rich querying, multi-collection, auto-scaling
  ├── Low-latency real-time sync, simple key-value data
  │   └── Realtime Database — lower latency, simpler pricing (bandwidth), WebSocket-native
  └── I need both
      └── Use Firestore for structured data, RTDB for presence/live cursors

Data Model: Subcollection vs Top-Level?

How does this data relate and grow?
  ├── 1:few relationship (<100 items, bounded), always accessed together
  │   └── Subcollection — query within parent document context
  ├── 1:many relationship (unbounded), independent queries needed
  │   └── Top-level collection — separate queries, no parent dependency
  └── Many:many or cross-collection needs
      └── Top-level collection + composite indexes

Cloud Function Trigger Location?

Where in the data lifecycle?
  ├── After document write → onCreate, onUpdate, onDelete
  ├── On HTTP request → onRequest (Express-style handler)
  ├── On schedule → pubsub.schedule (cron-like)
  ├── On auth event → functions.auth.user().onCreate
  └── On storage event → functions.storage.object().onFinalize

Workflow

Step 1: Project Setup

npm install firebase firebase-admin
npm install -g firebase-tools

firebase login
firebase init
# Select: Firestore, Functions, Storage, Hosting, Emulators
// src/lib/firebase.ts (client SDK)
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);
export const db = getFirestore(app);
export const auth = getAuth(app);
export const storage = getStorage(app);

// Admin SDK (server-side)
import { initializeApp as initializeAdmin, cert } from 'firebase-admin/app';
import { getFirestore as getAdminFirestore } from 'firebase-admin/firestore';
import { getAuth as getAdminAuth } from 'firebase-admin/auth';

const adminApp = initializeAdmin({ credential: cert(serviceAccount) });
export const adminDb = getAdminFirestore(adminApp);
export const adminAuth = getAdminAuth(adminApp);

Step 2: Firestore Data Modeling

Collection: users/{userId}
  name, email, avatar, createdAt, role

Collection: posts/{postId}
  title, content, authorId, tags[], published, likeCount, createdAt
  Subcollection: comments/{commentId}
    authorId, content, createdAt

Collection: tags/{tagId}
  name, postCount

Step 3: Security Rules

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    function isAuth() { return request.auth != null; }
    function isOwner(userId) { return request.auth.uid == userId; }
    function isAdmin() { return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == 'admin'; }

    match /users/{userId} {
      allow read: if true;
      allow write: if isOwner(userId) || isAdmin();
      allow create: if isAuth() && request.resource.data.email is string;
    }

    match /posts/{postId} {
      allow read: if true;
      allow create: if isAuth() && request.resource.data.authorId == request.auth.uid;
      allow update, delete: if isOwner(resource.data.authorId) || isAdmin();
    }

    match /posts/{postId}/comments/{commentId} {
      allow read: if true;
      allow write: if isAuth();
    }
  }
}

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/{allPaths=**} {
      allow read: if true;
      allow write: if request.auth.uid == userId;
    }
  }
}

Step 4: Cloud Functions

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';

admin.initializeApp();

export const onPostCreate = functions.firestore
  .document('posts/{postId}')
  .onCreate(async (snap, context) => {
    const post = snap.data();
    await admin.firestore().doc(`users/${post.authorId}`).update({
      postCount: admin.firestore.FieldValue.increment(1),
    });
  });

export const scheduledCleanup = functions.pubsub
  .schedule('every 24 hours')
  .onRun(async () => {
    const old = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000);
    const snaps = await admin.firestore()
      .collection('posts')
      .where('createdAt', '<', old)
      .where('published', '==', false)
      .get();
    const batch = admin.firestore().batch();
    snaps.forEach(doc => batch.delete(doc.ref));
    await batch.commit();
  });

Step 5: Authentication

// Email/password sign up
import { createUserWithEmailAndPassword } from 'firebase/auth';
await createUserWithEmailAndPassword(auth, email, password);

// Google sign in
import { GoogleAuthProvider, signInWithPopup } from 'firebase/auth';
const provider = new GoogleAuthProvider();
await signInWithPopup(auth, provider);

// Custom claims (Admin SDK)
await adminAuth.setCustomUserClaims(uid, { role: 'admin' });

// Verify ID token
const decoded = await adminAuth.verifyIdToken(idToken);
if (decoded.role === 'admin') { /* allow */ }

Step 6: Firestore Query Patterns

// Efficient queries — always use existing indexes
const posts = await adminDb
  .collection('posts')
  .where('published', '==', true)
  .where('tags', 'array-contains', 'javascript')
  .orderBy('createdAt', 'desc')
  .limit(20)
  .get();

// Pagination with cursors
const firstPage = await adminDb
  .collection('posts')
  .orderBy('createdAt', 'desc')
  .limit(10)
  .get();

const lastVisible = firstPage.docs[firstPage.docs.length - 1];
const secondPage = await adminDb
  .collection('posts')
  .orderBy('createdAt', 'desc')
  .startAfter(lastVisible)
  .limit(10)
  .get();

// Aggregation (use counters, don't count docs)
async function updatePostCount(userId: string, delta: number) {
  await adminDb.runTransaction(async (tx) => {
    const ref = adminDb.doc(`counters/${userId}`);
    const snap = await tx.get(ref);
    const current = snap.data()?.postCount ?? 0;
    tx.set(ref, { postCount: current + delta }, { merge: true });
  });
}

Step 7: Batched Writes and Transactions

// Batched write (atomic, up to 500 operations)
async function createPostWithTags(post: Post, tagIds: string[]) {
  const batch = adminDb.batch();
  const postRef = adminDb.collection('posts').doc();
  batch.set(postRef, { ...post, createdAt: admin.firestore.FieldValue.serverTimestamp() });

  for (const tagId of tagIds) {
    const tagRef = adminDb.doc(`tags/${tagId}`);
    batch.update(tagRef, { postCount: admin.firestore.FieldValue.increment(1) });
  }

  await batch.commit();
}

// Transaction (read-then-write, strong consistency)
async function transferPoints(fromId: string, toId: string, amount: number) {
  await adminDb.runTransaction(async (tx) => {
    const fromRef = adminDb.doc(`users/${fromId}`);
    const toRef = adminDb.doc(`users/${toId}`);
    const fromSnap = await tx.get(fromRef);
    const toSnap = await tx.get(toRef);

    if (fromSnap.data()!.points < amount) {
      throw new Error('Insufficient points');
    }

    tx.update(fromRef, { points: admin.firestore.FieldValue.increment(-amount) });
    tx.update(toRef, { points: admin.firestore.FieldValue.increment(amount) });
  });
}

Step 8: Cost Optimization

StrategyImpactImplementation
Limit document reads90% cost reductionPaginate, cache, avoid listing all docs
Use collection group queries instead of subcollection per docAvoid per-doc reads@firebase/ Firestore collection group indexes
Denormalize frequently-read dataFewer readsStore user displayName directly in post documents
Avoid listening to large collectionsUnbounded readsAlways filter with where() clauses
Use Firestore bundle for static dataZero reads for cached dataGenerate bundles on schedule, deploy to CDN
Shard hot documents at >1 write/sAvoid contentionAdd random suffix to document IDs
Set TTL policiesAuto-delete old dataFirestore TTL policy on collections

Step 9: Firebase Extensions

Use built-in extensions to reduce custom code:

  • Resize Images: auto-resize on storage upload
  • Translate Text: auto-translate Firestore fields using Cloud Translation
  • Trigger Email: send emails from Firestore writes
  • Delete User Data: cascade delete when auth user is removed
  • Firestore Stripe Subscriptions: manage subscriptions via Firestore writes

Production Considerations

ConcernPractice
Firestore max 1 write/s per docUse distributed counters for hot documents
Cold start for FunctionsSet minInstances for latency-sensitive endpoints. Budget impact: cost per idle instance
Function timeoutsSet timeout: 60s for HTTP, 540s for event-driven. Increase for heavy processing
Memory allocation256MB for light triggers, 1GB+ for image processing, 2GB+ for ML/model loading
Emulator suiteRun locally for development. Never develop against production
Point-in-time recoveryEnable PITR (7d) for production projects. Additional cost
App CheckEnable to block unauthorized client requests. Supports reCAPTCHA, App Attest, SafetyNet
Secret managementStore service account JSON in Secret Manager, never in repo

Security

RiskMitigation
Unsecured FirestoreValidate security rules with Firebase emulator. Deploy rules before app
Over-permissive read rulesAlways scope reads with auth checks. Don't allow read: if true for private data
Client-side validation bypassSecurity rules validate every request independently
Function endpoint abuseCallable functions verify auth automatically. HTTP functions must validate
Storage public accessUse signed URLs with expiry for private files. Block public by default
API key exposureFirebase API keys are public-by-design. Use App Check for additional security

Anti-Patterns

Anti-PatternWhy It's BadFix
Deeply nested data >20 levelsCannot query, hard to secureUse subcollections or top-level collections
Counting documents with get()O(N) reads, expensiveUse distributed counter or maintained count field
Listening to entire collectionUnbounded reads and updatesAlways use where() filters
Reading document in security rule for every access1 extra read per accessUse custom claims for role info
Functions without error handlingSilent failures, debugging nightmareAlways wrap in try/catch, log errors
Huge batched writes (>500 ops)Batch limit is 500 operationsSplit into multiple batches or use more targeted updates
Storing arrays for data that growsCannot atomically modify large arraysUse subcollection or map with keys
Over-indexingEach index adds write costOnly create indexes for actual queries

Rules

  • Firestore: max 1 write per second on a document — use aggregations for counters.
  • Use subcollections instead of nested objects when data grows unbounded.
  • Security rules evaluate on every read/write — limit get() calls within rules.
  • Cloud Functions: set memory/timeout based on workload — 256MB for light triggers, 1GB+ for heavy processing.
  • Firebase Pricing: Firestore charges per read/write/delete — index writes count as 1 write + 1 per index.
  • Use Firebase App Check to block unauthorized client requests.
  • Enable Firestore PITR (point-in-time recovery) for production.
  • Functions: use minInstances for latency-sensitive endpoints (cost trade-off).
  • Store service account JSON outside the repo — use secret manager.
  • Every Firebase SDK call from client must be scoped by security rules.
  • Denormalize for read performance — data duplication is acceptable in Firestore.
  • Always use emulator for development. Never point a client SDK at production Firestore during development.

Implementation Patterns

Firestore Repository

import { Firestore, CollectionReference, DocumentData, Query } from 'firebase/firestore';

interface Entity {
  id?: string;
  createdAt?: Date;
  updatedAt?: Date;
}

class FirestoreRepository<T extends Entity> {
  constructor(
    private firestore: Firestore,
    private collectionPath: string
  ) {}

  get collection(): CollectionReference<T> {
    return this.firestore.collection(this.collectionPath) as CollectionReference<T>;
  }

  async create(data: Omit<T, 'id' | 'createdAt' | 'updatedAt'>): Promise<string> {
    const docRef = await this.collection.add({
      ...data,
      createdAt: new Date(),
      updatedAt: new Date(),
    } as any);
    return docRef.id;
  }

  async get(id: string): Promise<T | null> {
    const doc = await this.collection.doc(id).get();
    return doc.exists ? { id: doc.id, ...doc.data() } as T : null;
  }

  async update(id: string, data: Partial<Omit<T, 'id'>>): Promise<void> {
    await this.collection.doc(id).update({
      ...data,
      updatedAt: new Date(),
    } as any);
  }

  async delete(id: string): Promise<void> {
    await this.collection.doc(id).delete();
  }

  async query(queries: ((ref: CollectionReference) => Query)[]): Promise<T[]> {
    let q: Query = this.collection;
    for (const queryFn of queries) {
      q = queryFn(this.collection);
    }
    const snapshot = await q.get();
    return snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }) as T);
  }
}

Architecture Decision Trees

Firebase When to Use

Should you use Firebase?
├── Small to medium app (< 100K users)
│   ├── Need real-time sync → Firestore
│   ├── Need auth → Firebase Auth (social providers, email)
│   └── Need file storage → Firebase Storage
│
├── MVP / Prototype
│   └── Firebase = fastest time to ship
│
├── Complex queries, joins, aggregations
│   └── Consider a relational DB (Firestore limits queries)
│
└── Heavy server-side processing
    └── Cloud Functions + Firebase or use a backend framework

Anti-Patterns

Anti-PatternWhy It FailsCorrect Approach
Deeply nested collections (>3 levels)Slow queries, high read costShallow collections with references
Missing composite indexesQueries fail with vague errorsCreate indexes when query requires them
Storing arrays for growing dataCan't atomically modify large arraysUse subcollections or maps
Not using emulatorsCosts accumulate, slow iterationFirestore emulator for development
Functions without error handlingSilent failuresAlways wrap in try/catch, log errors
Over-indexingEach index adds write costOnly index for actual queries

Performance Optimization

  • Batch reads in parallel: Use getAll() to fetch multiple documents in one batch. Reduces latency from N sequential reads to 1 parallel batch.
  • Data denormalization for read performance: Duplicate data across documents to avoid joins. Accept data duplication — Firestore charges per read, not per storage.

Implementation Patterns

Observer Pattern for Event Handling

` interface EventObserver { onEvent(event: T): Promise; }

class EventBus { private observers: Set<EventObserver> = new Set(); subscribe(observer: EventObserver): void { this.observers.add(observer); } unsubscribe(observer: EventObserver): void { this.observers.delete(observer); } async emit(event: T): Promise { const results = Array.from(this.observers).map(o => o.onEvent(event)); await Promise.allSettled(results); } } `

Configuration-Driven Approach

config: defaults: timeout: 30s retryCount: 3 overrides: production: timeout: 60s retryCount: 5 development: timeout: 300s retryCount: 1

Production Considerations

Deployment Checklist

  • Configuration validated against schema before startup
  • Health check endpoints registered and monitored
  • Graceful shutdown with draining period (30s timeout)
  • Resource limits configured (CPU, memory, file descriptors)
  • Log level set appropriate for environment
  • Metrics endpoint secured and exposed
  • Rate limiting configured per-tier
  • TLS certificates valid and auto-renewing
  • Database migrations run as separate deployment step
  • Feature flags ready for gradual rollout

Monitoring and Alerting

MetricThresholdSeverityAction
Error rate> 1% over 5minCriticalPage on-call
p99 latency> 2s over 5minWarningInvestigate
Throughput drop> 50% over 1minCriticalCheck upstream
Queue depth> 1000 over 1minWarningScale consumers
Disk usage> 85%WarningClean or expand
Memory usage> 90% heapCriticalRestart or scale

Anti-Patterns

Anti-PatternSymptomRoot CauseSolution
Premature optimizationComplex code for no measured benefitGuessing instead of profilingMeasure first, optimize based on data
Copy-paste reuseDuplicate code across codebaseLack of abstractionExtract shared logic into libraries
Gold-platingFeatures with no current requirementOver-engineeringYAGNI — build what's needed now
Magical thinkingAssumptions without validationSkipping error handlingHandle all failure modes explicitly

Performance Optimization

Caching Strategy

Cache hierarchy: L1 (in-memory local) → L2 (distributed Redis/Memcached) → L3 (CDN/Edge). Cache invalidation: TTL-based (simple, stale), event-based (complex, fresh), write-through (consistent, higher write latency), write-behind (fast writes, eventual consistency).

Resource Pooling

  • Database connections: Pool of reusable connections (HikariCP, pgBouncer)
  • HTTP connections: Keep-alive + connection pooling for external calls
  • Thread pool: Bounded thread pools for async task execution

Profiling Methodology

  1. Establish baseline with production traffic profile
  2. Profile CPU with sampling profiler (pprof, perf, async-profiler)
  3. Profile memory with heap dumps and allocation tracking
  4. Profile I/O with strace/perf trace for syscall analysis
  5. Profile latency with distributed tracing (OpenTelemetry)
  6. Identify bottleneck, formulate hypothesis, implement fix
  7. Re-profile to verify improvement, repeat

Security Considerations

Threat Modeling (STRIDE)

  • Spoofing: Identity validation, authentication
  • Tampering: Integrity checks, digital signatures
  • Repudiation: Audit logs, non-repudiation
  • Information disclosure: Encryption, access control
  • Denial of service: Rate limiting, resource quotas
  • Elevation of privilege: Principle of least privilege

Supply Chain Security

  • Dependency scanning: Snyk, Dependabot, Trivy
  • SBOM generation: CycloneDX or SPDX format
  • Signed commits: GPG or SSH commit signing
  • Artifact verification: Checksum validation, signature verification

Secrets Management

  • Secrets never in code — always in secrets manager (Vault, AWS Secrets Manager)
  • Rotation policy: Rotate database credentials every 90 days
  • Access audit: Log every secrets access, alert on anomalies
  • Encryption at rest and in transit for all secrets
  • Principle of least privilege: each service gets only its own secrets

Rules

  • Default-deny security posture — allow only explicitly required access.
  • All inputs validated, all outputs encoded, all errors handled.
  • Defend in depth — multiple layers of security controls.
  • Fail securely — errors default to safe behavior.
  • Log security-relevant events for audit and investigation.
  • Keep dependencies updated — automate vulnerability scanning.
  • Design for observability from day one, not as an afterthought.
  • Document all architectural decisions with rationale.
  • Review code for security, performance, and correctness before merging.
我的收藏