Appearance
Firebase - Google's Backend-as-a-Service Platform
Firebase represents Google's comprehensive Backend-as-a-Service (BaaS) platform, designed to accelerate application development by providing a suite of cloud-based tools and services. Originally developed by Firebase Inc. and acquired by Google in 2014, Firebase has evolved into a complete development platform that handles backend infrastructure, real-time data synchronization, authentication, hosting, analytics, and much more. Firebase offers two primary database solutions: the original Realtime Database and the more advanced Cloud Firestore, both designed to support real-time applications with offline capabilities and seamless synchronization across multiple clients.
Getting Started with Firebase
Project Setup and Configuration
javascript
// Install Firebase CLI
npm install -g firebase-tools
// Login to Firebase
firebase login
// Initialize Firebase project
firebase init
// Install Firebase SDK for web
npm install firebase
// Initialize Firebase in your application
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
import { getAuth } from 'firebase/auth';
import { getStorage } from 'firebase/storage';
const firebaseConfig = {
apiKey: "your-api-key",
authDomain: "your-project.firebaseapp.com",
databaseURL: "https://your-project-default-rtdb.firebaseio.com",
projectId: "your-project-id",
storageBucket: "your-project.appspot.com",
messagingSenderId: "123456789",
appId: "your-app-id"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
// Initialize services
const db = getFirestore(app);
const auth = getAuth(app);
const storage = getStorage(app);
export { db, auth, storage };
Firebase Console and Project Management
javascript
// Firebase project structure
// - Authentication: User management and sign-in
// - Firestore Database: NoSQL document database
// - Realtime Database: JSON tree database
// - Storage: File storage service
// - Hosting: Web hosting service
// - Functions: Serverless functions
// - Analytics: App usage analytics
// - Performance: App performance monitoring
// Environment configuration
// Development
const devConfig = {
apiKey: "dev-api-key",
authDomain: "dev-project.firebaseapp.com",
projectId: "dev-project-id"
};
// Production
const prodConfig = {
apiKey: "prod-api-key",
authDomain: "prod-project.firebaseapp.com",
projectId: "prod-project-id"
};
const config = process.env.NODE_ENV === 'production' ? prodConfig : devConfig;
const app = initializeApp(config);
Cloud Firestore (Recommended)
Database Structure and Collections
javascript
// Firestore data model: Collections -> Documents -> Fields
// Collections contain documents
// Documents contain fields and subcollections
// Documents are identified by unique IDs
import {
collection,
doc,
addDoc,
setDoc,
getDoc,
getDocs,
updateDoc,
deleteDoc,
query,
where,
orderBy,
limit,
onSnapshot
} from 'firebase/firestore';
// Reference to a collection
const usersRef = collection(db, 'users');
// Reference to a specific document
const userRef = doc(db, 'users', 'user123');
// Reference to a subcollection
const postsRef = collection(db, 'users', 'user123', 'posts');
// Create document with auto-generated ID
const createUser = async (userData) => {
try {
const docRef = await addDoc(collection(db, 'users'), {
name: userData.name,
email: userData.email,
createdAt: new Date(),
isActive: true,
profile: {
bio: userData.bio || '',
avatar: userData.avatar || '',
preferences: {
theme: 'light',
notifications: true
}
}
});
console.log('Document written with ID: ', docRef.id);
return docRef.id;
} catch (error) {
console.error('Error adding document: ', error);
throw error;
}
};
// Create document with custom ID
const createUserWithId = async (userId, userData) => {
try {
await setDoc(doc(db, 'users', userId), {
name: userData.name,
email: userData.email,
createdAt: new Date(),
isActive: true
});
console.log('Document created with custom ID: ', userId);
} catch (error) {
console.error('Error creating document: ', error);
throw error;
}
};
CRUD Operations
javascript
// CREATE - Add documents
const addPost = async (userId, postData) => {
try {
const postRef = await addDoc(collection(db, 'users', userId, 'posts'), {
title: postData.title,
content: postData.content,
tags: postData.tags || [],
publishedAt: new Date(),
isPublished: false,
likes: 0,
comments: []
});
return postRef.id;
} catch (error) {
console.error('Error adding post: ', error);
throw error;
}
};
// READ - Get single document
const getUser = async (userId) => {
try {
const docSnap = await getDoc(doc(db, 'users', userId));
if (docSnap.exists()) {
return { id: docSnap.id, ...docSnap.data() };
} else {
console.log('No such document!');
return null;
}
} catch (error) {
console.error('Error getting document: ', error);
throw error;
}
};
// READ - Get multiple documents
const getAllUsers = async () => {
try {
const querySnapshot = await getDocs(collection(db, 'users'));
const users = [];
querySnapshot.forEach((doc) => {
users.push({ id: doc.id, ...doc.data() });
});
return users;
} catch (error) {
console.error('Error getting documents: ', error);
throw error;
}
};
// UPDATE - Update document
const updateUser = async (userId, updates) => {
try {
const userRef = doc(db, 'users', userId);
await updateDoc(userRef, {
...updates,
updatedAt: new Date()
});
console.log('Document updated successfully');
} catch (error) {
console.error('Error updating document: ', error);
throw error;
}
};
// UPDATE - Update nested fields
const updateUserProfile = async (userId, profileUpdates) => {
try {
const userRef = doc(db, 'users', userId);
await updateDoc(userRef, {
'profile.bio': profileUpdates.bio,
'profile.avatar': profileUpdates.avatar,
'profile.preferences.theme': profileUpdates.theme,
updatedAt: new Date()
});
} catch (error) {
console.error('Error updating profile: ', error);
throw error;
}
};
// DELETE - Delete document
const deleteUser = async (userId) => {
try {
await deleteDoc(doc(db, 'users', userId));
console.log('Document deleted successfully');
} catch (error) {
console.error('Error deleting document: ', error);
throw error;
}
};
Querying and Filtering
javascript
import {
query,
where,
orderBy,
limit,
startAt,
endAt,
startAfter,
endBefore
} from 'firebase/firestore';
// Simple queries
const getActiveUsers = async () => {
const q = query(
collection(db, 'users'),
where('isActive', '==', true)
);
const querySnapshot = await getDocs(q);
return querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
};
// Compound queries
const getRecentActivePosts = async () => {
const q = query(
collection(db, 'posts'),
where('isPublished', '==', true),
where('publishedAt', '>', new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)),
orderBy('publishedAt', 'desc'),
limit(10)
);
const querySnapshot = await getDocs(q);
return querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
};
// Array queries
const getPostsByTags = async (tags) => {
const q = query(
collection(db, 'posts'),
where('tags', 'array-contains-any', tags)
);
const querySnapshot = await getDocs(q);
return querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
};
// Range queries
const getUsersByAgeRange = async (minAge, maxAge) => {
const q = query(
collection(db, 'users'),
where('age', '>=', minAge),
where('age', '<=', maxAge),
orderBy('age')
);
const querySnapshot = await getDocs(q);
return querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
};
// Pagination
const getPaginatedUsers = async (pageSize = 10, lastDoc = null) => {
let q = query(
collection(db, 'users'),
orderBy('createdAt', 'desc'),
limit(pageSize)
);
if (lastDoc) {
q = query(
collection(db, 'users'),
orderBy('createdAt', 'desc'),
startAfter(lastDoc),
limit(pageSize)
);
}
const querySnapshot = await getDocs(q);
const users = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
const lastVisible = querySnapshot.docs[querySnapshot.docs.length - 1];
return { users, lastDoc: lastVisible, hasMore: users.length === pageSize };
};
// Text search (limited - consider Algolia for full-text search)
const searchUsersByName = async (searchTerm) => {
const q = query(
collection(db, 'users'),
where('name', '>=', searchTerm),
where('name', '<=', searchTerm + '\uf8ff'),
orderBy('name'),
limit(10)
);
const querySnapshot = await getDocs(q);
return querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
};
Real-time Listeners
javascript
// Real-time listener for single document
const subscribeToUser = (userId, callback) => {
const userRef = doc(db, 'users', userId);
const unsubscribe = onSnapshot(userRef, (doc) => {
if (doc.exists()) {
callback({ id: doc.id, ...doc.data() });
} else {
callback(null);
}
}, (error) => {
console.error('Error listening to user: ', error);
});
return unsubscribe; // Call this function to stop listening
};
// Real-time listener for collection
const subscribeToActiveUsers = (callback) => {
const q = query(
collection(db, 'users'),
where('isActive', '==', true),
orderBy('createdAt', 'desc')
);
const unsubscribe = onSnapshot(q, (querySnapshot) => {
const users = [];
querySnapshot.forEach((doc) => {
users.push({ id: doc.id, ...doc.data() });
});
callback(users);
}, (error) => {
console.error('Error listening to users: ', error);
});
return unsubscribe;
};
// Listen to document changes with metadata
const subscribeToUserWithMetadata = (userId, callback) => {
const userRef = doc(db, 'users', userId);
const unsubscribe = onSnapshot(userRef, { includeMetadataChanges: true }, (doc) => {
const source = doc.metadata.hasPendingWrites ? 'Local' : 'Server';
const data = doc.exists() ? { id: doc.id, ...doc.data() } : null;
callback({ data, source, fromCache: doc.metadata.fromCache });
});
return unsubscribe;
};
// React hook example for real-time data
const useUser = (userId) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!userId) {
setUser(null);
setLoading(false);
return;
}
const unsubscribe = subscribeToUser(userId, (userData) => {
setUser(userData);
setLoading(false);
setError(null);
});
return () => unsubscribe();
}, [userId]);
return { user, loading, error };
};
Transactions and Batch Operations
javascript
import {
runTransaction,
writeBatch,
increment,
arrayUnion,
arrayRemove,
serverTimestamp
} from 'firebase/firestore';
// Transaction example - Transfer points between users
const transferPoints = async (fromUserId, toUserId, points) => {
try {
await runTransaction(db, async (transaction) => {
const fromUserRef = doc(db, 'users', fromUserId);
const toUserRef = doc(db, 'users', toUserId);
const fromUserDoc = await transaction.get(fromUserRef);
const toUserDoc = await transaction.get(toUserRef);
if (!fromUserDoc.exists() || !toUserDoc.exists()) {
throw new Error('One or both users do not exist');
}
const fromUserPoints = fromUserDoc.data().points || 0;
if (fromUserPoints < points) {
throw new Error('Insufficient points');
}
transaction.update(fromUserRef, {
points: fromUserPoints - points,
updatedAt: serverTimestamp()
});
transaction.update(toUserRef, {
points: increment(points),
updatedAt: serverTimestamp()
});
});
console.log('Points transferred successfully');
} catch (error) {
console.error('Transaction failed: ', error);
throw error;
}
};
// Batch operations
const batchUpdateUsers = async (userUpdates) => {
const batch = writeBatch(db);
userUpdates.forEach(({ userId, updates }) => {
const userRef = doc(db, 'users', userId);
batch.update(userRef, {
...updates,
updatedAt: serverTimestamp()
});
});
try {
await batch.commit();
console.log('Batch update completed');
} catch (error) {
console.error('Batch update failed: ', error);
throw error;
}
};
// Array operations
const addTagToPost = async (postId, tag) => {
const postRef = doc(db, 'posts', postId);
await updateDoc(postRef, {
tags: arrayUnion(tag),
updatedAt: serverTimestamp()
});
};
const removeTagFromPost = async (postId, tag) => {
const postRef = doc(db, 'posts', postId);
await updateDoc(postRef, {
tags: arrayRemove(tag),
updatedAt: serverTimestamp()
});
};
// Increment/decrement operations
const likePost = async (postId) => {
const postRef = doc(db, 'posts', postId);
await updateDoc(postRef, {
likes: increment(1),
updatedAt: serverTimestamp()
});
};
Firebase Realtime Database
Database Structure and References
javascript
import {
getDatabase,
ref,
set,
get,
push,
update,
remove,
on,
off,
child,
orderByChild,
orderByKey,
orderByValue,
limitToFirst,
limitToLast,
startAt,
endAt,
equalTo
} from 'firebase/database';
// Initialize Realtime Database
const rtdb = getDatabase();
// Database structure (JSON tree)
// {
// "users": {
// "user1": {
// "name": "John Doe",
// "email": "john@example.com",
// "posts": {
// "post1": true,
// "post2": true
// }
// }
// },
// "posts": {
// "post1": {
// "title": "First Post",
// "content": "Hello World",
// "author": "user1",
// "timestamp": 1642694400000
// }
// }
// }
// Create references
const usersRef = ref(rtdb, 'users');
const userRef = ref(rtdb, 'users/user1');
const postsRef = ref(rtdb, 'posts');
CRUD Operations in Realtime Database
javascript
// CREATE/UPDATE - Set data
const createUser = async (userId, userData) => {
try {
await set(ref(rtdb, `users/${userId}`), {
name: userData.name,
email: userData.email,
createdAt: Date.now(),
isActive: true
});
console.log('User created successfully');
} catch (error) {
console.error('Error creating user: ', error);
throw error;
}
};
// CREATE - Push data (auto-generated key)
const addPost = async (postData) => {
try {
const newPostRef = push(ref(rtdb, 'posts'));
await set(newPostRef, {
title: postData.title,
content: postData.content,
author: postData.author,
timestamp: Date.now(),
likes: 0
});
return newPostRef.key;
} catch (error) {
console.error('Error adding post: ', error);
throw error;
}
};
// READ - Get data once
const getUser = async (userId) => {
try {
const snapshot = await get(ref(rtdb, `users/${userId}`));
if (snapshot.exists()) {
return { id: userId, ...snapshot.val() };
} else {
console.log('No data available');
return null;
}
} catch (error) {
console.error('Error getting user: ', error);
throw error;
}
};
// READ - Get all users
const getAllUsers = async () => {
try {
const snapshot = await get(ref(rtdb, 'users'));
if (snapshot.exists()) {
const users = [];
snapshot.forEach((childSnapshot) => {
users.push({
id: childSnapshot.key,
...childSnapshot.val()
});
});
return users;
} else {
return [];
}
} catch (error) {
console.error('Error getting users: ', error);
throw error;
}
};
// UPDATE - Update specific fields
const updateUser = async (userId, updates) => {
try {
await update(ref(rtdb, `users/${userId}`), {
...updates,
updatedAt: Date.now()
});
console.log('User updated successfully');
} catch (error) {
console.error('Error updating user: ', error);
throw error;
}
};
// DELETE - Remove data
const deleteUser = async (userId) => {
try {
await remove(ref(rtdb, `users/${userId}`));
console.log('User deleted successfully');
} catch (error) {
console.error('Error deleting user: ', error);
throw error;
}
};
Real-time Listeners and Queries
javascript
// Real-time listener
const subscribeToUser = (userId, callback) => {
const userRef = ref(rtdb, `users/${userId}`);
const unsubscribe = on(userRef, 'value', (snapshot) => {
const data = snapshot.exists() ? { id: userId, ...snapshot.val() } : null;
callback(data);
}, (error) => {
console.error('Error listening to user: ', error);
});
return () => off(userRef, 'value', unsubscribe);
};
// Listen to child events
const subscribeToUserPosts = (userId, callbacks) => {
const userPostsRef = ref(rtdb, `users/${userId}/posts`);
const childAddedListener = on(userPostsRef, 'child_added', callbacks.onAdded);
const childChangedListener = on(userPostsRef, 'child_changed', callbacks.onChanged);
const childRemovedListener = on(userPostsRef, 'child_removed', callbacks.onRemoved);
return () => {
off(userPostsRef, 'child_added', childAddedListener);
off(userPostsRef, 'child_changed', childChangedListener);
off(userPostsRef, 'child_removed', childRemovedListener);
};
};
// Queries with ordering and filtering
const getTopPosts = async (limit = 10) => {
try {
const topPostsQuery = query(
ref(rtdb, 'posts'),
orderByChild('likes'),
limitToLast(limit)
);
const snapshot = await get(topPostsQuery);
const posts = [];
snapshot.forEach((childSnapshot) => {
posts.unshift({
id: childSnapshot.key,
...childSnapshot.val()
});
});
return posts;
} catch (error) {
console.error('Error getting top posts: ', error);
throw error;
}
};
// Range queries
const getPostsByDateRange = async (startDate, endDate) => {
try {
const dateRangeQuery = query(
ref(rtdb, 'posts'),
orderByChild('timestamp'),
startAt(startDate),
endAt(endDate)
);
const snapshot = await get(dateRangeQuery);
const posts = [];
snapshot.forEach((childSnapshot) => {
posts.push({
id: childSnapshot.key,
...childSnapshot.val()
});
});
return posts;
} catch (error) {
console.error('Error getting posts by date range: ', error);
throw error;
}
};
// Equal to query
const getUsersByStatus = async (status) => {
try {
const statusQuery = query(
ref(rtdb, 'users'),
orderByChild('status'),
equalTo(status)
);
const snapshot = await get(statusQuery);
const users = [];
snapshot.forEach((childSnapshot) => {
users.push({
id: childSnapshot.key,
...childSnapshot.val()
});
});
return users;
} catch (error) {
console.error('Error getting users by status: ', error);
throw error;
}
};
Firebase Authentication
Authentication Setup and Methods
javascript
import {
getAuth,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signInWithPopup,
GoogleAuthProvider,
FacebookAuthProvider,
TwitterAuthProvider,
signOut,
onAuthStateChanged,
updateProfile,
updatePassword,
sendPasswordResetEmail,
deleteUser
} from 'firebase/auth';
const auth = getAuth();
// Email/Password Authentication
const signUpWithEmail = async (email, password, displayName) => {
try {
const userCredential = await createUserWithEmailAndPassword(auth, email, password);
const user = userCredential.user;
// Update user profile
await updateProfile(user, {
displayName: displayName
});
// Create user document in Firestore
await setDoc(doc(db, 'users', user.uid), {
email: user.email,
displayName: displayName,
createdAt: new Date(),
isActive: true
});
return user;
} catch (error) {
console.error('Error signing up: ', error);
throw error;
}
};
const signInWithEmail = async (email, password) => {
try {
const userCredential = await signInWithEmailAndPassword(auth, email, password);
return userCredential.user;
} catch (error) {
console.error('Error signing in: ', error);
throw error;
}
};
// Social Authentication
const signInWithGoogle = async () => {
try {
const provider = new GoogleAuthProvider();
provider.addScope('profile');
provider.addScope('email');
const result = await signInWithPopup(auth, provider);
const user = result.user;
// Check if user document exists, create if not
const userDoc = await getDoc(doc(db, 'users', user.uid));
if (!userDoc.exists()) {
await setDoc(doc(db, 'users', user.uid), {
email: user.email,
displayName: user.displayName,
photoURL: user.photoURL,
provider: 'google',
createdAt: new Date(),
isActive: true
});
}
return user;
} catch (error) {
console.error('Error signing in with Google: ', error);
throw error;
}
};
// Authentication state observer
const useAuthState = () => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setUser(user);
setLoading(false);
});
return () => unsubscribe();
}, []);
return { user, loading };
};
// Sign out
const signOutUser = async () => {
try {
await signOut(auth);
console.log('User signed out successfully');
} catch (error) {
console.error('Error signing out: ', error);
throw error;
}
};
// Password reset
const resetPassword = async (email) => {
try {
await sendPasswordResetEmail(auth, email);
console.log('Password reset email sent');
} catch (error) {
console.error('Error sending password reset email: ', error);
throw error;
}
};
// Update user profile
const updateUserProfile = async (updates) => {
try {
const user = auth.currentUser;
if (user) {
await updateProfile(user, updates);
// Also update Firestore document
await updateDoc(doc(db, 'users', user.uid), {
...updates,
updatedAt: new Date()
});
}
} catch (error) {
console.error('Error updating profile: ', error);
throw error;
}
};
Security Rules
javascript
// Firestore Security Rules
// rules_version = '2';
// service cloud.firestore {
// match /databases/{database}/documents {
// // Users can read and write their own data
// match /users/{userId} {
// allow read, write: if request.auth != null && request.auth.uid == userId;
// }
//
// // Posts are readable by all authenticated users
// // Only the author can write/update/delete
// match /posts/{postId} {
// allow read: if request.auth != null;
// allow create: if request.auth != null &&
// request.auth.uid == resource.data.authorId;
// allow update, delete: if request.auth != null &&
// request.auth.uid == resource.data.authorId;
// }
//
// // Comments can be created by authenticated users
// // Only the author can update/delete their comments
// match /posts/{postId}/comments/{commentId} {
// allow read: if request.auth != null;
// allow create: if request.auth != null;
// allow update, delete: if request.auth != null &&
// request.auth.uid == resource.data.authorId;
// }
// }
// }
// Realtime Database Security Rules
// {
// "rules": {
// "users": {
// "$uid": {
// ".read": "$uid === auth.uid",
// ".write": "$uid === auth.uid"
// }
// },
// "posts": {
// ".read": "auth != null",
// "$postId": {
// ".write": "auth != null && (
// !data.exists() ||
// data.child('authorId').val() === auth.uid
// )"
// }
// }
// }
// }
// Custom claims for role-based access
const setUserRole = async (uid, role) => {
// This would be done in a Cloud Function with Admin SDK
// admin.auth().setCustomUserClaims(uid, { role: role });
};
// Check user role in security rules
// allow write: if request.auth.token.role == 'admin';
Firebase Storage
File Upload and Management
javascript
import {
getStorage,
ref as storageRef,
uploadBytes,
uploadBytesResumable,
getDownloadURL,
deleteObject,
listAll,
getMetadata,
updateMetadata
} from 'firebase/storage';
const storage = getStorage();
// Upload file
const uploadFile = async (file, path) => {
try {
const fileRef = storageRef(storage, path);
const snapshot = await uploadBytes(fileRef, file);
const downloadURL = await getDownloadURL(snapshot.ref);
console.log('File uploaded successfully');
return { downloadURL, fullPath: snapshot.ref.fullPath };
} catch (error) {
console.error('Error uploading file: ', error);
throw error;
}
};
// Upload with progress tracking
const uploadFileWithProgress = (file, path, onProgress) => {
return new Promise((resolve, reject) => {
const fileRef = storageRef(storage, path);
const uploadTask = uploadBytesResumable(fileRef, file);
uploadTask.on('state_changed',
(snapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
onProgress(progress);
},
(error) => {
console.error('Upload error: ', error);
reject(error);
},
async () => {
try {
const downloadURL = await getDownloadURL(uploadTask.snapshot.ref);
resolve({ downloadURL, fullPath: uploadTask.snapshot.ref.fullPath });
} catch (error) {
reject(error);
}
}
);
});
};
// Upload user avatar
const uploadUserAvatar = async (userId, file) => {
try {
const path = `avatars/${userId}/${file.name}`;
const result = await uploadFile(file, path);
// Update user profile with avatar URL
await updateDoc(doc(db, 'users', userId), {
photoURL: result.downloadURL,
updatedAt: new Date()
});
return result.downloadURL;
} catch (error) {
console.error('Error uploading avatar: ', error);
throw error;
}
};
// Delete file
const deleteFile = async (filePath) => {
try {
const fileRef = storageRef(storage, filePath);
await deleteObject(fileRef);
console.log('File deleted successfully');
} catch (error) {
console.error('Error deleting file: ', error);
throw error;
}
};
// List files in directory
const listFiles = async (path) => {
try {
const listRef = storageRef(storage, path);
const result = await listAll(listRef);
const files = await Promise.all(
result.items.map(async (itemRef) => {
const url = await getDownloadURL(itemRef);
const metadata = await getMetadata(itemRef);
return {
name: itemRef.name,
fullPath: itemRef.fullPath,
downloadURL: url,
size: metadata.size,
contentType: metadata.contentType,
timeCreated: metadata.timeCreated
};
})
);
return files;
} catch (error) {
console.error('Error listing files: ', error);
throw error;
}
};
Firebase Cloud Functions
Function Development and Deployment
javascript
// Install Firebase Functions
// npm install -g firebase-tools
// firebase init functions
// functions/index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// HTTP Cloud Function
exports.helloWorld = functions.https.onRequest((request, response) => {
response.send('Hello from Firebase!');
});
// Firestore Trigger
exports.onUserCreate = functions.firestore
.document('users/{userId}')
.onCreate(async (snap, context) => {
const userData = snap.data();
const userId = context.params.userId;
// Send welcome email
console.log(`New user created: ${userData.email}`);
// Create user stats document
await admin.firestore().collection('userStats').doc(userId).set({
postsCount: 0,
likesReceived: 0,
joinedAt: admin.firestore.FieldValue.serverTimestamp()
});
});
// Scheduled Function
exports.dailyCleanup = functions.pubsub
.schedule('0 2 * * *') // Every day at 2 AM
.timeZone('America/New_York')
.onRun(async (context) => {
// Clean up old data
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - 30);
const oldPosts = await admin.firestore()
.collection('posts')
.where('createdAt', '<', cutoffDate)
.where('isDeleted', '==', true)
.get();
const batch = admin.firestore().batch();
oldPosts.docs.forEach(doc => {
batch.delete(doc.ref);
});
await batch.commit();
console.log(`Deleted ${oldPosts.size} old posts`);
});
// Callable Function
exports.addAdminRole = functions.https.onCall(async (data, context) => {
// Check if user is authenticated and is admin
if (!context.auth) {
throw new functions.https.HttpsError('unauthenticated', 'User must be authenticated');
}
if (!context.auth.token.admin) {
throw new functions.https.HttpsError('permission-denied', 'User must be admin');
}
try {
await admin.auth().setCustomUserClaims(data.uid, { admin: true });
return { message: 'Admin role added successfully' };
} catch (error) {
throw new functions.https.HttpsError('internal', 'Error adding admin role');
}
});
// Deploy functions
// firebase deploy --only functions
Performance Optimization and Best Practices
Data Modeling Best Practices
javascript
// Firestore data modeling principles
// 1. Denormalization for read efficiency
const createPostWithAuthor = async (postData, authorData) => {
const postRef = await addDoc(collection(db, 'posts'), {
title: postData.title,
content: postData.content,
// Denormalize author data for efficient reads
author: {
id: authorData.id,
name: authorData.name,
avatar: authorData.avatar
},
createdAt: new Date(),
likes: 0,
commentsCount: 0
});
return postRef.id;
};
// 2. Use subcollections for one-to-many relationships
const addCommentToPost = async (postId, commentData) => {
const commentRef = await addDoc(
collection(db, 'posts', postId, 'comments'),
{
content: commentData.content,
author: commentData.author,
createdAt: new Date(),
likes: 0
}
);
// Update comment count in parent post
await updateDoc(doc(db, 'posts', postId), {
commentsCount: increment(1)
});
return commentRef.id;
};
// 3. Use arrays for small lists, subcollections for large lists
const addTagToPost = async (postId, tag) => {
await updateDoc(doc(db, 'posts', postId), {
tags: arrayUnion(tag)
});
};
// 4. Optimize for your queries
// Create composite indexes for compound queries
// Index: collection: posts, fields: isPublished ASC, createdAt DESC
const getPublishedPosts = async () => {
const q = query(
collection(db, 'posts'),
where('isPublished', '==', true),
orderBy('createdAt', 'desc'),
limit(20)
);
return await getDocs(q);
};
Caching and Offline Support
javascript
import { enableNetwork, disableNetwork } from 'firebase/firestore';
// Enable offline persistence
import { initializeFirestore, persistentLocalCache } from 'firebase/firestore';
const db = initializeFirestore(app, {
localCache: persistentLocalCache()
});
// Handle online/offline state
const handleNetworkChange = async (isOnline) => {
try {
if (isOnline) {
await enableNetwork(db);
console.log('Network enabled');
} else {
await disableNetwork(db);
console.log('Network disabled');
}
} catch (error) {
console.error('Error changing network state: ', error);
}
};
// Listen for network changes
window.addEventListener('online', () => handleNetworkChange(true));
window.addEventListener('offline', () => handleNetworkChange(false));
// Cache management
const getCachedData = async (docRef) => {
try {
// Try to get from cache first
const cachedDoc = await getDoc(docRef, { source: 'cache' });
if (cachedDoc.exists()) {
return { data: cachedDoc.data(), fromCache: true };
}
} catch (error) {
console.log('No cached data available');
}
// Fallback to server
const serverDoc = await getDoc(docRef, { source: 'server' });
return { data: serverDoc.exists() ? serverDoc.data() : null, fromCache: false };
};
Security and Performance Monitoring
javascript
// Performance monitoring
import { getPerformance, trace } from 'firebase/performance';
const perf = getPerformance(app);
// Custom trace
const measureDatabaseOperation = async (operation) => {
const customTrace = trace(perf, 'database_operation');
customTrace.start();
try {
const result = await operation();
customTrace.putAttribute('success', 'true');
return result;
} catch (error) {
customTrace.putAttribute('success', 'false');
customTrace.putAttribute('error', error.message);
throw error;
} finally {
customTrace.stop();
}
};
// Usage
const getUserWithTrace = async (userId) => {
return await measureDatabaseOperation(async () => {
return await getDoc(doc(db, 'users', userId));
});
};
// Analytics
import { getAnalytics, logEvent } from 'firebase/analytics';
const analytics = getAnalytics(app);
// Log custom events
const logUserAction = (action, parameters = {}) => {
logEvent(analytics, action, {
...parameters,
timestamp: Date.now()
});
};
// Usage
logUserAction('post_created', {
post_id: 'post123',
category: 'technology'
});
Firebase's comprehensive suite of services provides developers with a powerful platform for building modern, scalable applications with real-time capabilities, robust authentication, and seamless offline support. Its integration with Google Cloud Platform and extensive SDK support across multiple platforms makes it an excellent choice for rapid application development, from simple prototypes to complex, production-ready applications serving millions of users.