
Cloud
Learning Level
Advanced Firestore patterns enable handling large-scale data, complex transactions, and sophisticated query patterns required for enterprise applications.
By the end of this lesson, you'll understand:
const SHARD_COLLECTION_SIZE = 10;
async function incrementCounter(counterId) {
const shardId = Math.floor(Math.random() * SHARD_COLLECTION_SIZE);
const shardRef = doc(db, 'counters', counterId, 'shards', String(shardId));
await updateDoc(shardRef, {
value: increment(1)
});
}
async function getCounter(counterId) {
const snapshot = await getDocs(
collection(db, 'counters', counterId, 'shards')
);
let count = 0;
snapshot.forEach(doc => {
count += doc.data().value || 0;
});
return count;
}// For collections receiving > 1000 writes/sec
async function addDocument(data) {
const shardId = Math.floor(Math.random() * 20);
const shardRef = collection(db, 'users_shard_' + shardId);
await addDoc(shardRef, data);
}
async function queryShardedCollection(whereCondition) {
const results = [];
const shardCount = 20;
const promises = [];
for (let i = 0; i < shardCount; i++) {
const q = query(
collection(db, 'users_shard_' + i),
whereCondition
);
promises.push(getDocs(q));
}
const snapshots = await Promise.all(promises);
snapshots.forEach(snapshot => {
snapshot.forEach(doc => results.push({id: doc.id, ...doc.data()}));
});
return results;
}async function transferPoints(fromUserId, toUserId, points) {
const result = await runTransaction(db, async (transaction) => {
const fromRef = doc(db, 'users', fromUserId);
const toRef = doc(db, 'users', toUserId);
const fromSnap = await transaction.get(fromRef);
const toSnap = await transaction.get(toRef);
if (!fromSnap.exists() || !toSnap.exists()) {
throw new Error('User not found');
}
const fromPoints = fromSnap.data().points || 0;
if (fromPoints < points) {
throw new Error('Insufficient points');
}
transaction.update(fromRef, {
points: fromPoints - points,
lastTransfer: serverTimestamp()
});
transaction.update(toRef, {
points: (toSnap.data().points || 0) + points,
lastReceived: serverTimestamp()
});
// Create transaction record
const txnRef = collection(db, 'transactions');
transaction.set(doc(txnRef), {
from: fromUserId,
to: toUserId,
points,
timestamp: serverTimestamp()
});
return {success: true, points};
});
return result;
}async function bulkUpdateUsers(userIds, updateData) {
const batchSize = 500; // Firestore limit is 500 per batch
for (let i = 0; i < userIds.length; i += batchSize) {
const batch = writeBatch(db);
const chunk = userIds.slice(i, i + batchSize);
chunk.forEach(userId => {
const userRef = doc(db, 'users', userId);
batch.update(userRef, updateData);
});
await batch.commit();
console.log(`Committed batch ${Math.floor(i / batchSize) + 1}`);
}
}
// Bulk import data
async function bulkImportDocuments(collectionName, documents) {
const batchSize = 500;
for (let i = 0; i < documents.length; i += batchSize) {
const batch = writeBatch(db);
const chunk = documents.slice(i, i + batchSize);
chunk.forEach((doc, idx) => {
const ref = doc.id
? doc(db, collectionName, doc.id)
: doc(db, collectionName);
batch.set(ref, doc.data || doc);
});
await batch.commit();
}
}// Composite index for complex queries
async function findActiveUsers(country, subscription) {
// Requires composite index on (country, subscription, createdAt)
const q = query(
collection(db, 'users'),
where('country', '==', country),
where('subscription', '==', subscription),
where('isActive', '==', true),
orderBy('createdAt', 'desc'),
limit(100)
);
const snapshot = await getDocs(q);
return snapshot.docs.map(doc => ({id: doc.id, ...doc.data()}));
}
// Alternative: Denormalization approach
async function findActiveUsersDenormalized(country, subscription) {
// Single collection with composite field
const q = query(
collection(db, 'users_active'),
where('country_subscription', '==', `${country}:${subscription}`),
orderBy('createdAt', 'desc'),
limit(100)
);
return getDocs(q);
}class DenormalizationManager {
async createUser(userData) {
const userRef = doc(collection(db, 'users'));
await setDoc(userRef, {
id: userRef.id,
...userData,
// Denormalized data for fast queries
country_subscription: `${userData.country}:${userData.subscription}`,
active_badge: userData.subscription === 'premium' && userData.isActive,
lastUpdated: serverTimestamp()
});
return userRef.id;
}
async updateUserRole(userId, newRole) {
const userRef = doc(db, 'users', userId);
// Update user document
await updateDoc(userRef, { role: newRole });
// Also update denormalized copies in other collections
const postsSnapshot = await getDocs(
query(collection(db, 'posts'), where('authorId', '==', userId))
);
const batch = writeBatch(db);
postsSnapshot.forEach(postDoc => {
batch.update(postDoc.ref, { authorRole: newRole });
});
await batch.commit();
}
}firestore.rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Function: Check if user owns document
function isOwner(userId) {
return request.auth.uid == resource.data.owner;
}
// Function: Check if user has role
function hasRole(role) {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == role;
}
// Admin access to all data
match /{document=**} {
allow read, write: if hasRole('admin');
}
// User can only access own data
match /users/{userId} {
allow read, write: if isOwner(userId);
allow create: if request.auth.uid == userId;
}
// Collaborative collections with role-based access
match /workspaces/{workspaceId} {
allow read: if request.auth.uid in resource.data.members;
allow write: if isOwner(request.auth.uid) && hasRole('workspace_owner');
match /documents/{documentId} {
allow read: if request.auth.uid in get(/databases/$(database)/documents/workspaces/$(workspaceId)).data.members;
allow write: if get(/databases/$(database)/documents/workspaces/$(workspaceId)).data.owner == request.auth.uid;
}
}
}
}Learn about Firestore migration patterns from other databases, or explore integration with BigQuery for analytics.
Resources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep • Build Real • Verify Skills • Launch Forward