Reduce Firestore read costs with proven tactics: read only necessary fields, paginate, cache data, use aggregate queries, optimize indexes, and monitor usage.

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Step 1: Understand Firestore Read Operations
Firestore charges you for every document read during a query. This includes both direct document reads and any reads that occur from queries or when you listen to snapshot updates.
Step 2: Use Projections to Read Only Necessary Fields
Firestore allows you to specify only the fields you need when reading documents, which reduces the amount of data read.
const citiesRef = db.collection('cities');
const snapshot = await citiesRef.select('name', 'country').get();
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
Step 3: Leverage Pagination and Batch Reads
Instead of reading all documents at once, paginate the results to limit the number of reads per operation.
const citiesRef = db.collection('cities');
let query = citiesRef.orderBy('population').limit(25);
let snapshot = await query.get();
// Process the documents in snapshot...
// Use the last document as the start for the next query
let last = snapshot.docs[snapshot.docs.length - 1];
// Construct a new query starting at this document
let next = citiesRef.orderBy('population').startAfter(last).limit(25);
let nextSnapshot = await next.get();
Step 4: Use Caching to Minimize Database Reads
Firestore provides built-in local caching, which allows your app to use data already read from Firestore.
firebase.firestore().settings({
cacheSizeBytes: firebase.firestore.CACHE_SIZE_UNLIMITED
});
// Enable offline persistence
firebase.firestore().enablePersistence()
.catch((err) => {
if (err.code == 'failed-precondition') {
console.warn("Persistence can only be enabled in one tab at a time");
} else if (err.code == 'unimplemented') {
console.warn("This browser does not support offline persistence");
}
});
Step 5: Optimize Query Performance
Optimizing the structure of your queries can reduce the number of unnecessary reads.
const citiesRef = db.collection('cities');
const query = citiesRef.where('state', '==', 'CA').where('population', '>', 1000000);
const snapshot = await query.get();
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
Step 6: Use Aggregate Queries Where Possible
If you need only aggregated data (like counts), use Firestore's aggregation queries to minimize document reads.
const citiesRef = db.collectionGroup('cities');
const aggregateSnapshot = await citiesRef.count().get();
console.log(`There are ${aggregateSnapshot.data().count} city documents.`);
Step 7: Monitor and Analyze Firestore Usage
Use Firebase Console to watch real-time usage metrics and see what is causing high read operations.
Step 8: Use Cloud Functions to Reduce Client Reads
Shift some logic to Cloud Functions, which can read data once, process it, and send the required amount of data back to the client.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.getFilteredCities = functions.https.onRequest((req, res) => {
const citiesRef = admin.firestore().collection('cities');
citiesRef.where('state', '==', 'CA').get()
.then(snapshot => {
let cities = [];
snapshot.forEach(doc => {
cities.push(doc.data());
});
res.status(200).send(cities);
})
.catch(error => res.status(500).send(error));
});
Step 9: Use Indexed Fields in Queries
Properly indexed fields ensure queries execute with less computational cost, reducing Firestore's read counts.
Step 10: Avoid Unnecessary Snapshot Listeners
Listeners can cause multiple reads. Optimize their usage by detaching them when not needed.
const unsub = db.collection('cities').onSnapshot(snapshot => {
// Process new changes
});
// Later when no longer needed
unsub();
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.