
Firebase Vertex Ai
- 50 installs
- 2.6k repo stars
- Updated August 5, 2026
- jeremylongshore/claude-code-plugins-plus-skills
Build and deploy Firebase apps (Auth, Firestore, Functions, Hosting) that call Vertex AI and Gemini from Cloud Functions with secure secrets handling.
About
Operates Firebase projects end-to-end and integrates Gemini/Vertex AI from Cloud Functions with least-privilege IAM and secrets management. A developer uses it to ship AI-powered Firebase backends.
- Wires Cloud Functions to Gemini/Vertex AI with Secret Manager
- Covers Firestore rules, Auth, deploy, and smoke tests
Firebase Vertex Ai by the numbers
- 50 all-time installs (skills.sh)
- Ranked #3,241 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/jeremylongshore/claude-code-plugins-plus-skills --skill firebase-vertex-aiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 50 |
|---|---|
| repo stars | ★ 2.6k |
| Last updated | August 5, 2026 |
| Repository | jeremylongshore/claude-code-plugins-plus-skills ↗ |
What it does
Build and deploy Firebase apps (Auth, Firestore, Functions, Hosting) that call Vertex AI and Gemini from Cloud Functions with secure secrets handling.
Files
Firebase Vertex AI
Operate Firebase projects end-to-end (Auth, Firestore, Functions, Hosting) and integrate Gemini/Vertex AI safely for AI-powered features.
Overview
Use this skill to design, implement, and deploy Firebase applications that call Vertex AI/Gemini from Cloud Functions (or other GCP services) with secure secrets handling, least-privilege IAM, and production-ready observability.
Prerequisites
- Node.js runtime and Firebase CLI access for the target project
- A Firebase project (billing enabled for Functions/Vertex AI as needed)
- Vertex AI API enabled and permissions to call Gemini/Vertex AI from your backend
- Secrets managed via env vars or Secret Manager (never in client code)
Instructions
1. Initialize Firebase (or validate an existing repo): Hosting/Functions/Firestore as required. 2. Implement backend integration:
- add a Cloud Function/HTTP endpoint that calls Gemini/Vertex AI
- validate inputs and return structured responses
3. Configure data and security:
- Firestore rules + indexes
- Storage rules (if applicable)
- Auth providers and authorization checks
4. Deploy and verify:
- deploy Functions/Hosting
- run smoke tests against deployed endpoints
5. Add ops guardrails:
- logging/metrics
- alerting for error spikes
- basic cost controls (budgets/quotas) where appropriate
Output
- A deployable Firebase project structure (configs + Functions/Hosting as needed)
- Secure backend code that calls Gemini/Vertex AI (with secrets handled correctly)
- Firestore/Storage rules and index guidance
- A verification checklist (local + deployed) and CI-ready commands
Error Handling
- Auth failures: identify the principal and missing permission/role; fix with least privilege.
- Billing/API issues: detect which API or quota is blocking and provide remediation steps.
- Firestore rule/index problems: provide minimal repro queries and rule fixes.
- Vertex AI call failures: surface model/region mismatches and add retries/backoff for transient errors.
Examples
Example: Gemini-backed chat API on Firebase
- Request: “Deploy Hosting + a Function that powers a Gemini chat endpoint.”
- Result:
/api/chatfunction, Secret Manager wiring, and smoke tests.
Example: Firestore-powered RAG
- Request: “Build a RAG flow that embeds docs and answers with citations.”
- Result: ingestion plan, embedding + index strategy, and evaluation prompts.
Resources
- Full detailed guide (kept for reference):
${CLAUDE_SKILL_DIR}/references/SKILL.full.md - Firebase docs: https://firebase.google.com/docs
- Cloud Functions for Firebase: https://firebase.google.com/docs/functions
- Vertex AI docs: https://cloud.google.com/vertex-ai/docs
ARD: Firebase Vertex AI Skill
Part of Tons of Skills by Intent Solutions | jeremylongshore.com
System Context
This skill operates within the Firebase ecosystem on Google Cloud Platform. The primary integration points are:
┌─────────────┐ ┌──────────────────┐ ┌───────────────┐
│ Client App │────▶│ Firebase Hosting │────▶│ Cloud Functions│
│ (Browser) │ │ (CDN + Rewrites) │ │ (Node.js 20) │
└─────────────┘ └──────────────────┘ └───────┬───────┘
│
┌──────────────────┐ │
│ Firebase Auth │◀─────────────┤
│ (Identity) │ │
└──────────────────┘ │
├──▶ Vertex AI (Gemini)
┌──────────────────┐ │
│ Cloud Firestore │◀─────────────┤
│ (Document DB) │ │
└──────────────────┘ │
│
┌──────────────────┐ │
│ Secret Manager │◀─────────────┘
│ (Credentials) │
└──────────────────┘External Systems
| System | Role | Interface |
|---|---|---|
| Firebase Auth | Identity provider, JWT token issuer | Admin SDK auth(), client SDK signInWith* |
| Cloud Firestore | Document database, security rules engine | Admin SDK firestore(), REST API |
| Cloud Functions | Serverless compute, HTTP/event triggers | firebase-functions SDK, HTTPS callable |
| Firebase Hosting | Static asset CDN, function rewrites | firebase.json rewrites config |
| Vertex AI | Gemini model inference (chat, embeddings) | @google-cloud/vertexai SDK |
| Secret Manager | API key and credential storage | firebase-functions secrets config |
| Cloud Storage | File storage with security rules | Admin SDK storage() |
Data Flow
Primary Flow: Client Request to Gemini Response
1. Client sends authenticated request to /api/chat
2. Firebase Hosting rewrites /api/** to Cloud Function
3. Cloud Function verifies Firebase Auth token (context.auth)
4. Function reads user context from Firestore (optional)
5. Function calls Vertex AI Gemini with prompt + context
6. Gemini returns generated content
7. Function stores result in Firestore (optional)
8. Function returns structured JSON to clientInitialization Flow
1. firebase init → select Functions, Firestore, Hosting, Emulators
2. Install @google-cloud/vertexai in functions/
3. Configure secrets: firebase functions:secrets:set VERTEX_API_KEY
4. Write firestore.rules with auth helpers
5. Write firestore.indexes.json for any composite queries
6. firebase emulators:start → verify locally
7. firebase deploy --only hosting,functions,firestore → productionEmbedding + RAG Flow
1. Document created in Firestore (posts/{postId})
2. Firestore onCreate trigger fires Cloud Function
3. Function calls Vertex AI text-embedding-004 model
4. Embedding vector stored in embeddings/{postId}
5. Query: user sends search text
6. Function generates query embedding
7. Firestore vector search finds nearest documents
8. Function calls Gemini with retrieved context + question
9. Gemini returns grounded answer with source referencesDesign Decisions
DD-1: Cloud Functions as AI Backend
Decision: All Vertex AI calls run in Cloud Functions, never from client-side code.
Rationale: Client-side Gemini calls would expose API keys in browser network traffic. Cloud Functions authenticate via service account identity (Application Default Credentials), eliminating key exposure. Functions also enable input validation, rate limiting, and structured logging before the model call.
Trade-off: Adds ~100ms network latency for the function hop. Acceptable given Gemini inference takes 1-4 seconds.
DD-2: Secret Manager for API Keys
Decision: Use defineSecret() from firebase-functions/v2 to bind secrets from Secret Manager at function deploy time.
Rationale: Environment variables in .env files risk being committed to version control. Firebase's defineSecret() integration provisions secrets in Secret Manager and injects them at runtime without filesystem exposure.
Example:
import { defineSecret } from "firebase-functions/params";
const vertexKey = defineSecret("VERTEX_API_KEY");
export const chat = onCall({ secrets: [vertexKey] }, async (req) => {
// vertexKey.value() available at runtime
});DD-3: Emulator-First Development
Decision: All generated code includes emulator configuration and local smoke test commands.
Rationale: Firebase Emulator Suite replicates Auth, Firestore, Functions, and Hosting locally. Testing against emulators avoids production data corruption, eliminates billing during development, and provides sub-second feedback. The firebase.json emulators block is generated with fixed ports to avoid conflicts.
Configuration:
{
"emulators": {
"auth": { "port": 9099 },
"functions": { "port": 5001 },
"firestore": { "port": 8080 },
"hosting": { "port": 5000 },
"ui": { "enabled": true, "port": 4000 }
}
}DD-4: Locked-Down Security Rules by Default
Decision: Generated firestore.rules deny all access by default. Each collection gets explicit, minimal rules.
Rationale: Firebase's default rules allow open access for 30 days after project creation, then lock down. Many tutorials leave rules open. This skill generates production-grade rules from the start with helper functions for isAuthenticated(), isOwner(), and hasRole().
DD-5: TypeScript for Cloud Functions
Decision: All generated Cloud Functions use TypeScript with strict mode.
Rationale: TypeScript catches type errors at build time (especially important for Vertex AI response parsing), provides better IDE support, and is the default for firebase init functions since Firebase CLI v12. The functions/tsconfig.json enables strict: true and targets es2022.
DD-6: Gemini Model Selection
Decision: Default to gemini-2.5-flash for general use; recommend gemini-2.5-pro for complex reasoning tasks.
Rationale: Flash provides the best latency/cost ratio for most Firebase use cases (chat, content analysis, moderation). Pro is reserved for multi-step reasoning or large-context RAG queries where quality matters more than speed.
Component Design
Cloud Function Structure
functions/
├── src/
│ ├── index.ts # Function exports (barrel file)
│ ├── config.ts # Project config, secret definitions
│ ├── middleware/
│ │ ├── auth.ts # Token verification helpers
│ │ └── validation.ts # Input schema validation (zod)
│ ├── vertex/
│ │ ├── client.ts # VertexAI client singleton
│ │ ├── chat.ts # Chat completion function
│ │ ├── embeddings.ts # Embedding generation
│ │ └── moderation.ts # Content moderation
│ └── triggers/
│ ├── on-user-create.ts # Auth trigger: profile creation
│ └── on-post-create.ts # Firestore trigger: auto-embed
├── package.json
└── tsconfig.jsonSecurity Rules Architecture
Rules use a layered approach: 1. Global helpers: isAuthenticated(), isOwner(uid), hasRole(role) 2. Collection rules: Each collection match block references helpers 3. Field validation: request.resource.data checks enforce schema at the rules layer 4. Admin override: hasRole('admin') provides emergency access without rule changes
Environment Strategy
| Environment | Firebase Project | Vertex AI Region | Purpose |
|---|---|---|---|
| local | demo-project (emulator) | N/A (mocked) | Development |
| dev | myapp-dev | us-central1 | Integration testing |
| staging | myapp-staging | us-central1 | Pre-production validation |
| prod | myapp-prod | us-central1 | Production traffic |
Project aliases managed via .firebaserc:
{
"projects": {
"default": "myapp-dev",
"staging": "myapp-staging",
"prod": "myapp-prod"
}
}Failure Modes and Recovery
| Failure | Detection | Recovery |
|---|---|---|
| Vertex AI quota exceeded | HTTP 429 from Gemini API | Exponential backoff with jitter; fallback to cached response |
| Function cold start > 10s | Cloud Monitoring latency metric | Set minInstances: 1 for critical functions |
| Firestore write contention | ABORTED error on transaction | Retry with exponential backoff (Admin SDK auto-retries) |
| Auth token expired | 401 from callable function | Client SDK auto-refreshes; function returns clear error |
| Secret not found | Function fails to start | firebase functions:secrets:set before deploy |
| Missing composite index | Firestore FAILED_PRECONDITION | Deploy firestore.indexes.json; error message includes exact index URL |
Observability
Structured Logging
import { logger } from "firebase-functions/v2";
logger.info("Gemini call completed", {
model: "gemini-2.5-flash",
inputTokens: usage.promptTokenCount,
outputTokens: usage.candidatesTokenCount,
latencyMs: Date.now() - startTime,
userId: context.auth?.uid,
});Key Metrics to Monitor
function/execution_countby function name and statusfunction/execution_timesp50/p95/p99- Vertex AI
aiplatform.googleapis.com/prediction/online/prediction_count - Firestore
firestore.googleapis.com/document/read_countandwrite_count - Billing alerts at 50%, 80%, 100% of monthly budget
PRD: Firebase Vertex AI Skill
Problem Statement
Building Firebase applications with Vertex AI integration requires coordinating multiple services (Auth, Firestore, Functions, Hosting) while managing secrets, IAM roles, security rules, and deployment pipelines. Developers face a steep learning curve assembling these pieces correctly, and mistakes in secrets management or IAM configuration lead to security vulnerabilities or production outages.
Common failure modes without this skill:
- API keys leaked into client-side code or version control
- Overly permissive Firestore/Storage security rules deployed to production
- Vertex AI calls made from client-side code instead of secure Cloud Functions
- Missing composite indexes discovered only after deployment
- No emulator testing, leading to slow feedback loops and costly mistakes
Target Users
| Persona | Description | Key Need |
|---|---|---|
| Full-stack developer | Building web/mobile apps on Firebase | End-to-end project scaffold with AI features |
| Firebase practitioner | Experienced with Firebase, new to Vertex AI | Safe Gemini integration patterns in Functions |
| GCP team lead | Managing multi-environment Firebase deployments | Security rules, IAM, and deployment automation |
| AI prototyper | Experimenting with Gemini for content analysis or RAG | Working Firebase backend with Vertex AI in < 10 min |
Success Criteria
1. Time to deploy: A developer with Firebase CLI installed can have a working Firebase project with a Gemini-backed Cloud Function deployed in under 10 minutes. 2. Security by default: Every generated project uses Secret Manager for API keys, least-privilege IAM roles, and non-trivial security rules. 3. Emulator-first: All generated code runs against Firebase Emulator Suite before touching production. 4. Zero leaked secrets: No API keys, service account JSON, or credentials appear in client code, logs, or version control. 5. Production readiness: Deployed Functions include error handling, structured logging, and retry logic for transient Vertex AI failures.
Scope
In Scope
- Firebase project initialization (firebase init with Functions, Firestore, Hosting, Emulators)
- Cloud Functions that call Vertex AI Gemini (chat, embeddings, content analysis)
- Firestore security rules and composite index generation
- Storage security rules with file-type and size constraints
- Auth provider configuration and custom claims for RBAC
- Secret Manager integration for API keys and service credentials
- Emulator Suite configuration and smoke test commands
- Single-command deployment (firebase deploy) with environment targeting
- Structured logging and basic cost alerting guidance
Out of Scope
- Firebase Extensions marketplace integration
- Firebase ML custom model training and deployment
- Firebase Analytics event design and conversion funnels
- Multi-region Firestore replication strategies
- Firebase A/B testing and Remote Config workflows
- Custom domain and SSL certificate management for Hosting
Functional Requirements
FR-1: Project Initialization
The skill must detect whether a Firebase project exists (presence of firebase.json) and either initialize a new project or validate the existing one. Initialization selects Functions (Node.js 20, TypeScript), Firestore, Hosting, and Emulators.
FR-2: Vertex AI Backend Function
The skill must generate a Cloud Function that:
- Imports
@google-cloud/vertexai - Reads the GCP project ID from environment configuration
- Calls a Gemini model (defaulting to
gemini-2.5-flash) - Validates input, returns structured JSON, and handles errors with appropriate HTTP status codes
FR-3: Security Configuration
The skill must produce:
firestore.ruleswith helper functions (isAuthenticated,isOwner,hasRole)firestore.indexes.jsonwith any composite indexes required by generated queriesstorage.ruleswith file-type and size constraints- Auth provider setup guidance (Email/Password at minimum)
FR-4: Secrets Management
The skill must use Firebase Functions secrets (backed by Secret Manager) for any API keys. No secrets may appear in firebase.json, source code literals, or .env files committed to version control.
FR-5: Emulator Testing
The skill must produce emulator configuration in firebase.json and provide commands to start emulators and run smoke tests against local endpoints.
FR-6: Deployment
The skill must generate a deployment command targeting the correct Firebase project alias (dev/staging/prod) and deploying only the changed services.
Non-Functional Requirements
- Latency: Cloud Functions calling Gemini should respond in < 5 seconds for typical prompts (< 500 tokens).
- Cost: Default function configuration uses
minInstances: 0to avoid idle billing. Budget alerts are documented. - Observability: Functions emit structured logs parseable by Cloud Logging. Error rates are surfaced via Cloud Monitoring.
- Portability: Generated code targets Node.js 20 LTS and uses only
@google-cloud/vertexaiandfirebase-adminSDKs.
Dependencies
| Dependency | Version | Purpose |
|---|---|---|
| Firebase CLI | >= 13.0 | Project init, deploy, emulators |
| Node.js | >= 20 LTS | Cloud Functions runtime |
| @google-cloud/vertexai | >= 1.0 | Gemini API access |
| firebase-admin | >= 12.0 | Firestore, Auth, Storage admin ops |
| firebase-functions | >= 5.0 | Cloud Functions triggers and config |
Risks and Mitigations
| Risk | Impact | Mitigation |
|---|---|---|
| Vertex AI API not enabled on project | Function deployment succeeds but calls fail | Pre-check: gcloud services list --enabled for aiplatform.googleapis.com |
| Billing not enabled | Functions and Vertex AI calls rejected | Detect billing status early; provide gcloud billing remediation |
| Region mismatch | Gemini model unavailable in selected region | Default to us-central1; document region availability |
| Security rules too permissive | Data exposure in production | Generate locked-down rules by default; warn on any allow read: if true |
| Cold start latency | First request > 10s | Document minInstances trade-off; default to 0 with guidance to increase |
Firebase Vertex AI: Error Reference
Firebase CLI Errors
| Error | Cause | Fix |
|---|---|---|
Error: Failed to authenticate | Firebase CLI not logged in or token expired | Run firebase login --reauth |
Error: No project active | No project selected in .firebaserc | Run firebase use <project-id> or firebase use --add |
Error: HTTP Error: 403, The caller does not have permission | Service account missing IAM roles | Grant roles/firebase.admin and roles/cloudfunctions.developer to the deploying principal |
Error: The default Firebase app does not exist | admin.initializeApp() not called before SDK use | Add admin.initializeApp() at top of index.ts before any admin SDK calls |
Error: Could not find or access firebase.json | Command run outside project root | cd to directory containing firebase.json |
Authentication Errors
| Error | Cause | Fix |
|---|---|---|
auth/configuration-not-found | Auth provider not enabled in Firebase Console | Enable the provider under Authentication > Sign-in method |
auth/invalid-custom-token | Custom token signed with wrong service account or expired | Verify the service account matches the Firebase project; tokens expire after 1 hour |
auth/id-token-expired | Client ID token older than 1 hour | Call getIdToken(true) to force refresh on the client |
auth/insufficient-permission | Admin SDK called without proper service account | Set GOOGLE_APPLICATION_CREDENTIALS or deploy to a Firebase-managed environment |
auth/user-not-found | UID referenced in custom claims or Firestore does not exist | Verify user exists with admin.auth().getUser(uid) before writing claims |
UNAUTHENTICATED (callable function) | Client did not send auth token with callable request | Ensure Firebase Auth is initialized on client and user is signed in before calling |
Cloud Functions Deployment Errors
| Error | Cause | Fix |
|---|---|---|
Error: Build failed | TypeScript compilation errors in functions/src/ | Run cd functions && npm run build locally to see errors |
Error: Function failed on loading user code | Missing dependency or broken import at runtime | Check functions/package.json includes all imports; run npm ci in functions/ |
Error: Quota exceeded for quota group 'default' | Too many function deployments in short period | Wait 1-2 minutes; deploy with --only functions:functionName to limit scope |
Error: Memory limit exceeded | Function uses more than configured memory (default 256MB) | Increase memory in function options: { memory: "512MiB" } |
Error: Function execution took X ms, finished with status: timeout | Function exceeded timeout (default 60s, max 540s) | Increase timeout: { timeoutSeconds: 300 }; optimize slow operations |
Error: Secret "X" was not found | Secret referenced in defineSecret() not created | Run firebase functions:secrets:set SECRET_NAME before deploying |
Error: Cannot deploy functions with experiments | Using v2 features not yet GA in your CLI version | Update Firebase CLI: npm install -g firebase-tools@latest |
Vertex AI API Errors
| Error | Cause | Fix |
|---|---|---|
PERMISSION_DENIED: Vertex AI API has not been used in project X | aiplatform.googleapis.com API not enabled | Run gcloud services enable aiplatform.googleapis.com --project=PROJECT_ID |
PERMISSION_DENIED: Missing IAM permission | Function's service account lacks Vertex AI roles | Grant roles/aiplatform.user to the function's runtime service account |
NOT_FOUND: Model not found: publishers/google/models/X | Invalid model name or model not available in region | Verify model name (e.g., gemini-2.5-flash); check regional availability |
RESOURCE_EXHAUSTED: Quota exceeded | Vertex AI requests/min or tokens/min quota hit | Implement exponential backoff; request quota increase in GCP Console |
INVALID_ARGUMENT: Request payload size exceeds the limit | Input prompt exceeds model context window | Truncate input; use gemini-2.5-pro for larger contexts (1M tokens) |
INTERNAL: An internal error has occurred | Transient Vertex AI service error | Retry with exponential backoff (initial delay 1s, max 3 retries) |
DEADLINE_EXCEEDED | Gemini inference took too long | Use gemini-2.5-flash for faster responses; reduce prompt size; increase function timeout |
FAILED_PRECONDITION: Billing is not enabled | GCP project has no billing account | Link a billing account at console.cloud.google.com/billing |
Firestore Errors
| Error | Cause | Fix |
|---|---|---|
PERMISSION_DENIED: Missing or insufficient permissions | Security rules block the operation | Check firestore.rules; test with firebase emulators:exec |
FAILED_PRECONDITION: The query requires an index | Composite index missing for the query | Click the link in the error message, or add the index to firestore.indexes.json and deploy |
ALREADY_EXISTS: Document already exists | Calling create() on existing document | Use set() with { merge: true } or check existence first |
NOT_FOUND: No document to update | Calling update() on non-existent document | Use set() instead, or verify document exists before update |
ABORTED: Transaction was aborted | Write contention on the same document | Retry automatically handled by Admin SDK; redesign to reduce contention on hot documents |
DEADLINE_EXCEEDED: Deadline exceeded | Firestore operation took > 60s | Reduce query scope; add indexes; check network connectivity |
RESOURCE_EXHAUSTED: Too many writes | Exceeding 10,000 writes/second per database | Distribute writes across document keys; use batch commits (max 500 ops) |
Hosting Errors
| Error | Cause | Fix |
|---|---|---|
Error: Hosting directory "dist" does not exist | Build output directory missing or wrong name | Run your build command first; verify hosting.public in firebase.json matches output dir |
Error: Function "api" is not a valid rewrite target | Function referenced in hosting rewrite not deployed | Deploy functions first: firebase deploy --only functions then --only hosting |
HTTP 404 on SPA routes | Missing catch-all rewrite for client-side routing | Add { "source": "**", "destination": "/index.html" } as last rewrite |
Billing and Quota Issues
| Error | Cause | Fix |
|---|---|---|
BILLING_DISABLED | Project on Spark (free) plan attempting paid operations | Upgrade to Blaze plan at console.firebase.google.com |
Cloud Functions requires Blaze plan | Functions not available on Spark plan | Upgrade to Blaze plan; set budget alerts to avoid surprises |
| Unexpected high bill | Unoptimized Firestore reads or runaway function invocations | Set budget alerts: gcloud billing budgets create; review usage in Firebase Console > Usage & billing |
Quota exceeded for quota metric 'Generate Content requests' | Vertex AI free tier or quota limit reached | Request quota increase in IAM & Admin > Quotas; implement client-side rate limiting |
--- [Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)
Firebase Vertex AI: Examples
Example 1: Gemini-Backed Chat API on Firebase
Cloud Function accepting user messages, calling Gemini, storing conversation history in Firestore.
// functions/src/vertex/chat.ts
import { onCall, HttpsError } from "firebase-functions/v2/https";
import { defineSecret } from "firebase-functions/params";
import { VertexAI } from "@google-cloud/vertexai";
import * as admin from "firebase-admin";
const projectId = defineSecret("GCP_PROJECT_ID");
const db = admin.firestore();
export const chat = onCall(
{ secrets: [projectId], memory: "512MiB", timeoutSeconds: 120 },
async (req) => {
if (!req.auth) throw new HttpsError("unauthenticated", "Sign in required");
const { message, conversationId } = req.data;
if (!message || typeof message !== "string" || message.length > 4000)
throw new HttpsError("invalid-argument", "Message must be 1-4000 chars");
const vertex = new VertexAI({ project: projectId.value(), location: "us-central1" });
const model = vertex.getGenerativeModel({ model: "gemini-2.5-flash" });
// Load conversation history
let history: { role: string; parts: { text: string }[] }[] = [];
if (conversationId) {
const convDoc = await db.collection("users").doc(req.auth.uid)
.collection("conversations").doc(conversationId).get();
if (convDoc.exists) history = convDoc.data()?.messages || [];
}
const chatSession = model.startChat({ history });
const result = await chatSession.sendMessage(message);
const responseText = result.response.candidates?.[0]?.content?.parts?.[0]?.text || "";
// Persist conversation
const convRef = conversationId
? db.collection("users").doc(req.auth.uid).collection("conversations").doc(conversationId)
: db.collection("users").doc(req.auth.uid).collection("conversations").doc();
await convRef.set({
messages: [...history,
{ role: "user", parts: [{ text: message }] },
{ role: "model", parts: [{ text: responseText }] }],
updatedAt: admin.firestore.FieldValue.serverTimestamp(),
}, { merge: true });
return { response: responseText, conversationId: convRef.id };
}
);Security rule: match /users/{userId}/conversations/{convId} { allow read, write: if request.auth.uid == userId; }
Test: firebase emulators:start --only functions,firestore,auth then curl -X POST http://localhost:5001/PROJECT/us-central1/chat -H "Content-Type: application/json" -d '{"data":{"message":"Hello"}}'
---
Example 2: Firestore-Powered RAG with Citations
Ingest documents as embeddings, answer questions with source attribution.
// Ingestion: Firestore onCreate trigger generates embeddings
export const onDocCreate = onDocumentCreated("documents/{docId}", async (event) => {
const snap = event.data;
if (!snap) return;
const { title, content } = snap.data();
const vertex = new VertexAI({ project: process.env.GCP_PROJECT_ID!, location: "us-central1" });
const embedModel = vertex.getGenerativeModel({ model: "text-embedding-004" });
const result = await embedModel.embedContent({
content: { role: "user", parts: [{ text: `${title}\n\n${content}` }] },
});
await db.collection("embeddings").doc(event.params.docId).set({
docId: event.params.docId, title,
vector: result.embedding.values,
createdAt: admin.firestore.FieldValue.serverTimestamp(),
});
});
// Query: vector search + Gemini answer
export const ragQuery = onCall({ memory: "1GiB", timeoutSeconds: 180 }, async (req) => {
if (!req.auth) throw new HttpsError("unauthenticated", "Sign in required");
const vertex = new VertexAI({ project: process.env.GCP_PROJECT_ID!, location: "us-central1" });
// Generate query embedding
const embedModel = vertex.getGenerativeModel({ model: "text-embedding-004" });
const qEmbed = await embedModel.embedContent({
content: { role: "user", parts: [{ text: req.data.query }] },
});
// Vector search
const results = await db.collection("embeddings")
.findNearest("vector", qEmbed.embedding.values, { limit: 5, distanceMeasure: "COSINE" })
.get();
// Fetch full docs and generate grounded answer
const sources = await Promise.all(results.docs.map(async (emb) => {
const doc = await db.collection("documents").doc(emb.data().docId).get();
return { id: emb.data().docId, title: emb.data().title, content: doc.data()?.content || "" };
}));
const genModel = vertex.getGenerativeModel({ model: "gemini-2.5-flash" });
const context = sources.map((s, i) => `[${i + 1}] ${s.title}: ${s.content}`).join("\n\n");
const result = await genModel.generateContent(
`Answer using sources. Cite as [1], [2].\n\nSources:\n${context}\n\nQuestion: ${req.data.query}`
);
return {
answer: result.response.candidates?.[0]?.content?.parts?.[0]?.text || "",
sources: sources.map((s, i) => ({ citation: i + 1, title: s.title, id: s.id })),
};
});---
Example 3: Content Moderation with Gemini in Firestore Trigger
Auto-moderate user-generated content on write, queue unsafe content for human review.
export const moderatePost = onDocumentCreated("posts/{postId}", async (event) => {
const snap = event.data;
if (!snap) return;
const { content, authorId } = snap.data();
const vertex = new VertexAI({ project: process.env.GCP_PROJECT_ID!, location: "us-central1" });
const model = vertex.getGenerativeModel({ model: "gemini-2.5-flash" });
const result = await model.generateContent({
contents: [{ role: "user", parts: [{ text:
`Evaluate for safety. Return JSON: {"safe": bool, "categories": string[], "confidence": number, "reason": string}\n\nContent: ${content}`
}] }],
generationConfig: { responseMimeType: "application/json" },
});
const mod = JSON.parse(result.response.candidates?.[0]?.content?.parts?.[0]?.text || "{}");
await snap.ref.update({
moderation: { safe: mod.safe ?? false, categories: mod.categories || [], confidence: mod.confidence || 0 },
visible: mod.safe === true,
});
if (!mod.safe) {
await db.collection("moderation_queue").add({
postId: event.params.postId, authorId, reason: mod.reason, flaggedAt: new Date().toISOString(),
});
}
});---
Example 4: Full-Stack Deploy with Auth + Functions + Hosting
Deployment script with environment targeting and smoke tests.
#!/bin/bash
set -euo pipefail
ENV="${1:-dev}"
firebase use "$ENV"
cd functions && npm ci && npm run build && cd ..
npm run build
# Deploy in dependency order
firebase deploy --only firestore:rules,storage:rules
firebase deploy --only firestore:indexes
firebase deploy --only functions
firebase deploy --only hosting
# Smoke test
PROJECT_ID=$(firebase use | grep -oP 'Active Project: \K\S+' || echo "unknown")
BASE_URL="https://${PROJECT_ID}.web.app"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL")
[ "$HTTP_CODE" = "200" ] && echo "PASS: Hosting OK" || { echo "FAIL: $HTTP_CODE"; exit 1; }
echo "Deployed: $BASE_URL"Required firebase.json:
{
"hosting": {
"public": "dist",
"rewrites": [
{ "source": "/api/**", "function": "api" },
{ "source": "**", "destination": "/index.html" }
]
},
"functions": [{ "source": "functions", "codebase": "default", "runtime": "nodejs20" }],
"firestore": { "rules": "firestore.rules", "indexes": "firestore.indexes.json" },
"storage": { "rules": "storage.rules" },
"emulators": {
"auth": { "port": 9099 }, "functions": { "port": 5001 },
"firestore": { "port": 8080 }, "hosting": { "port": 5000 },
"ui": { "enabled": true, "port": 4000 }
}
}--- [Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)
Firebase Vertex AI: Implementation Guide
Firebase Project Structure
project-root/
├── firebase.json # Service config, rewrites, emulators
├── .firebaserc # Project aliases (dev/staging/prod)
├── firestore.rules # Security rules (version controlled)
├── firestore.indexes.json # Composite indexes
├── storage.rules # Storage security rules
├── functions/
│ ├── package.json # @google-cloud/vertexai, firebase-admin
│ ├── tsconfig.json # strict: true, target: es2022
│ └── src/
│ ├── index.ts # Barrel file: exports all functions
│ ├── config.ts # Secret definitions, shared constants
│ ├── vertex/ # chat.ts, embeddings.ts, moderation.ts
│ ├── middleware/ # auth.ts, validation.ts
│ └── triggers/ # on-user-create.ts, on-post-create.ts
├── public/ or dist/ # Hosting static assets
└── .gitignore # Must include: .env*, serviceAccount*.jsonCloud Functions + Vertex AI Pattern
Singleton Client
Create one VertexAI instance per cold start, not per request:
// functions/src/vertex/client.ts
import { VertexAI, GenerativeModel } from "@google-cloud/vertexai";
let _vertex: VertexAI | null = null;
export function getVertex(projectId: string): VertexAI {
if (!_vertex) _vertex = new VertexAI({ project: projectId, location: "us-central1" });
return _vertex;
}
export function getChatModel(projectId: string): GenerativeModel {
return getVertex(projectId).getGenerativeModel({ model: "gemini-2.5-flash" });
}Secret Management
// functions/src/config.ts — define at module scope
import { defineSecret } from "firebase-functions/params";
export const gcpProjectId = defineSecret("GCP_PROJECT_ID");
// Provision: firebase functions:secrets:set GCP_PROJECT_ID
// functions/src/vertex/chat.ts — reference in function options
export const chat = onCall(
{ secrets: [gcpProjectId], memory: "512MiB", timeoutSeconds: 120 },
async (req) => { const model = getChatModel(gcpProjectId.value()); /* ... */ }
);Error Handling with Retry
async function callWithRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try { return await fn(); }
catch (err: any) {
const retryable = ["INTERNAL", "DEADLINE_EXCEEDED", "RESOURCE_EXHAUSTED"];
if (attempt === maxRetries || !retryable.includes(err.code)) throw err;
await new Promise((r) => setTimeout(r, Math.pow(2, attempt) * 1000 + Math.random() * 500));
}
}
throw new Error("Unreachable");
}Security Rules Design
Deny by default, allowlist per collection:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} { allow read, write: if false; }
function isAuth() { return request.auth != null; }
function isOwner(uid) { return isAuth() && request.auth.uid == uid; }
function hasRole(role) {
return isAuth() && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == role;
}
match /users/{userId} {
allow read: if isOwner(userId) || hasRole('admin');
allow create: if isOwner(userId);
allow update: if isOwner(userId) &&
!request.resource.data.diff(resource.data).affectedKeys().hasAny(['role', 'createdAt']);
}
match /posts/{postId} {
allow read: if true;
allow create: if isAuth() && request.resource.data.authorId == request.auth.uid
&& request.resource.data.keys().hasAll(['title', 'content', 'authorId'])
&& request.resource.data.title is string && request.resource.data.title.size() <= 200;
allow update: if isOwner(resource.data.authorId);
allow delete: if isOwner(resource.data.authorId) || hasRole('admin');
}
match /embeddings/{id} { allow read: if isAuth(); allow write: if hasRole('admin'); }
match /users/{userId}/conversations/{convId} { allow read, write: if isOwner(userId); }
}
}Emulator Testing Strategy
Configuration
{ "emulators": {
"auth": { "port": 9099 }, "functions": { "port": 5001 },
"firestore": { "port": 8080 }, "hosting": { "port": 5000 },
"storage": { "port": 9199 }, "ui": { "enabled": true, "port": 4000 }
}}Rules Unit Tests
import { initializeTestEnvironment, assertSucceeds, assertFails } from "@firebase/rules-unit-testing";
const testEnv = await initializeTestEnvironment({
projectId: "demo-test",
firestore: { rules: readFileSync("firestore.rules", "utf8") },
});
const alice = testEnv.authenticatedContext("alice");
await assertSucceeds(alice.firestore().collection("users").doc("alice").get());
const unauth = testEnv.unauthenticatedContext();
await assertFails(unauth.firestore().collection("users").doc("alice").get());Smoke Test
firebase emulators:start --only auth,functions,firestore &
sleep 5
curl -sf http://localhost:5001/demo-project/us-central1/health || { echo "FAIL"; exit 1; }
echo "PASS"Deployment Pipeline
Deploy in dependency order to avoid broken rewrites:
1. firebase deploy --only firestore:rules -- security rules first 2. firebase deploy --only firestore:indexes -- indexes (may take minutes) 3. firebase deploy --only storage:rules -- storage security 4. firebase deploy --only functions -- backend logic 5. firebase deploy --only hosting -- frontend (depends on function rewrites)
CI/CD with GitHub Actions
name: Deploy Firebase
on: { push: { branches: [main] } }
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: "20" }
- run: npm ci && cd functions && npm ci && npm run build && cd .. && npm run build && npm test
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
projectId: ${{ secrets.FIREBASE_PROJECT_ID }}
channelId: liveEnvironment Management
.firebaserc
{ "projects": { "default": "myapp-dev", "staging": "myapp-staging", "prod": "myapp-prod" } }Switching and Deploying
firebase use staging && firebase deploy --only functions
firebase use prod && firebase deploy --only functionsPer-Environment Secrets
Each Firebase project has its own Secret Manager. Set secrets per project:
firebase use staging && firebase functions:secrets:set GCP_PROJECT_ID
firebase use prod && firebase functions:secrets:set GCP_PROJECT_IDNon-Secret Environment Config
Use defineString for non-secret values, set via functions/.env.<project>:
import { defineString } from "firebase-functions/params";
const environment = defineString("ENVIRONMENT", { default: "dev" });# functions/.env.myapp-staging
ENVIRONMENT=staging
VERTEX_REGION=us-central1--- [Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)
#!/bin/bash
# init-firebase.sh - Initialize Firebase project with Vertex AI
set -euo pipefail
PROJECT_NAME="${1:-firebase-project}"
echo "Initializing Firebase Project with Vertex AI Integration"
echo "Project: $PROJECT_NAME"
echo ""
mkdir -p "$PROJECT_NAME"
cd "$PROJECT_NAME"
# Check Firebase CLI
if ! command -v firebase &> /dev/null; then
echo "Installing Firebase CLI..."
npm install -g firebase-tools
fi
# Initialize Firebase project
echo "Initializing Firebase..."
firebase init
# Create project structure
mkdir -p functions/src/{auth,firestore,vertex,storage}
mkdir -p public
# Create .env.local
cat > .env.local <<'EOF'
GCP_PROJECT_ID=your-project-id
GOOGLE_APPLICATION_CREDENTIALS=./service-account-key.json
EOF
# Create Vertex AI integration template
cat > functions/src/vertex/embeddings.ts <<'EOF'
import { VertexAI } from '@google-cloud/vertexai';
import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';
const vertex = new VertexAI({
project: process.env.GCP_PROJECT_ID!,
location: 'us-central1'
});
export const generateEmbeddings = functions.firestore
.document('posts/{postId}')
.onCreate(async (snap, context) => {
const post = snap.data();
const text = post.title + ' ' + post.content;
const model = vertex.getGenerativeModel({ model: 'text-embedding-004' });
const result = await model.embedText({ text });
await admin.firestore()
.collection('embeddings')
.doc(context.params.postId)
.set({
postId: context.params.postId,
vector: result.embedding.values,
createdAt: admin.firestore.FieldValue.serverTimestamp()
});
});
EOF
echo "✓ Firebase project initialized with Vertex AI integration"
echo ""
echo "Next steps:"
echo " cd $PROJECT_NAME"
echo " npm install --prefix functions"
echo " firebase deploy"