Firebase platform expert with Vertex AI Gemini integration for Authentication, Firestore, Storage, Functions, Hosting, and AI-powered features...
Production-ready Firebase platform operations specialist with deep Vertex AI Gemini integration expertise. Handles complete Firebase lifecycle including setup, development, deployment, security configuration, and AI-powered feature implementation.
Firebase CLI Setup:
# Install Firebase CLI
npm install -g firebase-tools
# Login to Firebase
firebase login
# Initialize project
firebase init
# Select features:
# - Hosting
# - Functions
# - Firestore
# - Storage
# - Emulators
Project Structure:
firebase-project/
├── firebase.json # Firebase configuration
├── .firebaserc # Project aliases
├── firestore.rules # Firestore security rules
├── firestore.indexes.json # Firestore indexes
├── storage.rules # Storage security rules
├── functions/ # Cloud Functions
│ ├── src/
│ │ ├── index.ts
│ │ ├── auth/ # Auth triggers
│ │ ├── firestore/ # Firestore triggers
│ │ └── vertex/ # Vertex AI functions
│ ├── package.json
│ └── tsconfig.json
├── public/ # Hosting files (or dist/)
├── .env.local # Environment variables
└── README.md
Setup Auth Providers:
// functions/src/auth/setup-auth.ts
import * as admin from 'firebase-admin';
admin.initializeApp();
// Create custom claims for role-based access
export const setUserRole = functions.https.onCall(async (data, context) => {
if (!context.auth?.token.admin) {
throw new functions.https.HttpsError(
'permission-denied',
'Only admins can set user roles'
);
}
await admin.auth().setCustomUserClaims(data.userId, {
role: data.role
});
return { success: true };
});
// Auth trigger: Create user profile on signup
export const onUserCreate = functions.auth.user().onCreate(async (user) => {
const userDoc = {
email: user.email,
displayName: user.displayName,
photoURL: user.photoURL,
createdAt: admin.firestore.FieldValue.serverTimestamp(),
role: 'user'
};
await admin.firestore()
.collection('users')
.doc(user.uid)
.set(userDoc);
});
Security Rules:
// firestore.rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Helper functions
function isAuthenticated() {
return request.auth != null;
}
function isOwner(userId) {
return isAuthenticated() && request.auth.uid == userId;
}
function hasRole(role) {
return isAuthenticated() &&
get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == role;
}
// User profiles
match /users/{userId} {
allow read: if isAuthenticated();
allow write: if isOwner(userId) || hasRole('admin');
}
// Public content
match /posts/{postId} {
allow read: if true;
allow create: if isAuthenticated() &&
request.resource.data.authorId == request.auth.uid;
allow update, delete: if isOwner(resource.data.authorId) || hasRole('admin');
}
// AI embeddings (for RAG)
match /embeddings/{embeddingId} {
allow read: if isAuthenticated();
allow write: if hasRole('admin');
}
}
}
Indexes Configuration:
// firestore.indexes.json
{
"indexes": [
{
"collectionGroup": "posts",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "authorId", "order": "ASCENDING" },
{ "fieldPath": "createdAt", "order": "DESCENDING" }
]
},
{
"collectionGroup": "embeddings",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "category", "order": "ASCENDING" },
{ "fieldPath": "vector", "mode": "VECTOR", "dimensions": 768 }
]
}
]
}
Embeddings Generation:
// functions/src/vertex/generate-embeddings.ts
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'
});
// Generate embeddings when document is created
export const generateEmbeddings = functions.firestore
.document('posts/{postId}')
.onCreate(async (snap, context) => {
const post = snap.data();
const text = post.title + ' ' + post.content;
// Generate embedding with Vertex AI
const model = vertex.getGenerativeModel({ model: 'text-embedding-004' });
const result = await model.embedText({ text });
// Store embedding in Firestore
await admin.firestore()
.collection('embeddings')
.doc(context.params.postId)
.set({
postId: context.params.postId,
vector: result.embedding.values,
createdAt: admin.firestore.FieldValue.serverTimestamp()
});
});
AI Content Analysis:
// functions/src/vertex/analyze-content.ts
import { VertexAI } from '@google-cloud/vertexai';
const vertex = new VertexAI({
project: process.env.GCP_PROJECT_ID!,
location: 'us-central1'
});
export const analyzeContent = functions.https.onCall(async (data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError('unauthenticated', 'User must be authenticated');
}
const model = vertex.getGenerativeModel({ model: 'gemini-2.0-flash-exp' });
const prompt = `
Analyze this content for:
- Sentiment (positive/negative/neutral)
- Category (technology/business/entertainment/etc)
- Summary (1-2 sentences)
- Key topics (up to 5)
Content:
${data.content}
Return as JSON with keys: sentiment, category, summary, topics
`;
const result = await model.generateContent(prompt);
const analysis = JSON.parse(result.response.text());
return analysis;
});
RAG System Implementation:
// functions/src/vertex/rag-query.ts
export const ragQuery = functions.https.onCall(async (data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError('unauthenticated', 'Authentication required');
}
// 1. Generate query embedding
const queryModel = vertex.getGenerativeModel({ model: 'text-embedding-004' });
const queryEmbedding = await queryModel.embedText({ text: data.query });
// 2. Find similar documents in Firestore
const embeddingsRef = admin.firestore().collection('embeddings');
const similarDocs = await embeddingsRef
.where('vector', 'near', {
vector: queryEmbedding.embedding.values,
distanceMeasure: 'COSINE',
limit: 5
})
.get();
// 3. Get full documents
const documents = await Promise.all(
similarDocs.docs.map(async (doc) => {
const postDoc = await admin.firestore()
.collection('posts')
.doc(doc.data().postId)
.get();
return postDoc.data();
})
);
// 4. Generate answer with Gemini using retrieved context
const genModel = vertex.getGenerativeModel({ model: 'gemini-2.0-flash-exp' });
const contextText = documents
.map(doc => `${doc.title}: ${doc.content}`)
.join('\n\n');
const prompt = `
Based on the following context, answer the user's question:
Context:
${contextText}
Question: ${data.query}
Provide a comprehensive answer based on the context. If the context doesn't contain enough information, say so.
`;
const result = await genModel.generateContent(prompt);
return {
answer: result.response.text(),
sources: documents.map(doc => ({ title: doc.title, id: doc.id }))
};
});
Security Rules:
// storage.rules
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
// User uploads
match /users/{userId}/{allPaths=**} {
allow read: if request.auth != null;
allow write: if request.auth.uid == userId &&
request.resource.size < 10 * 1024 * 1024 && // 10MB limit
request.resource.contentType.matches('image/.*');
}
// Public files
match /public/{allPaths=**} {
allow read: if true;
allow write: if request.auth != null &&
request.auth.token.admin == true;
}
}
}
File Upload Function:
// functions/src/storage/process-upload.ts
export const processUpload = functions.storage
.object()
.onFinalize(async (object) => {
const filePath = object.name!;
const contentType = object.contentType;
// Generate thumbnail for images
if (contentType?.startsWith('image/')) {
// Use Sharp or ImageMagick to create thumbnail
// Store in /thumbnails/ folder
}
// Update Firestore with file metadata
await admin.firestore()
.collection('files')
.add({
name: object.name,
size: object.size,
contentType: object.contentType,
url: `https://storage.googleapis.com/${object.bucket}/${object.name}`,
uploadedAt: admin.firestore.FieldValue.serverTimestamp()
});
});
Configuration:
// firebase.json
{
"hosting": {
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "/api/**",
"function": "api"
},
{
"source": "**",
"destination": "/index.html"
}
],
"headers": [
{
"source": "**/*.@(js|css)",
"headers": [
{
"key": "Cache-Control",
"value": "max-age=31536000"
}
]
}
]
},
"functions": {
"source": "functions",
"runtime": "nodejs20"
},
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"storage": {
"rules": "storage.rules"
}
}
Deploy Script:
#!/bin/bash
# deploy.sh - Deploy to Firebase
set -e
echo "Building application..."
npm run build
echo "Deploying to Firebase..."
firebase deploy --only hosting,functions,firestore:rules,storage:rules
echo "Deployment complete!"
firebase hosting:channel:list # Show preview channels
GitHub Actions:
# .github/workflows/firebase-deploy.yml
name: Deploy to Firebase
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to Firebase
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
projectId: ${{ secrets.FIREBASE_PROJECT_ID }}
channelId: live
# Local development
firebase emulators:start --only functions,firestore,hosting
# Deploy everything
firebase deploy
# Deploy specific services
firebase deploy --only hosting
firebase deploy --only functions
firebase deploy --only firestore:rules
# Hosting preview channels
firebase hosting:channel:deploy preview-branch
# View logs
firebase functions:log --only functionName
# Test security rules
firebase emulators:exec --only firestore "npm test"
See examples/ directory for:
This skill provides comprehensive Firebase platform expertise with deep Vertex AI integration for building production-ready applications.