How to Use Firestore Increment

One of the common challenges faced when working with Firestore is maintaining an accurate count of a value on a document (or multiple documents). One cannot simply update a counter and expect it to be accurate because many clients might be competing to update the same value simultaneously. The solution to this problem has traditionally been to deploy a Cloud Function that runs the update in a transaction to guarantee an atomic update. This works great, but adds a significant amount of complexity for a relatively simple requirement.

Fortunately, Firebase recently introduced FieldValue.increment to make this process much, much easier. Let’s take a look at how it works…

Firestore Increment

Increment is a special value that will atomically update a counter based on the interval you specify. You do not need to read the document to obtain the current total - Firebase does all the calculation serverside. In most cases, you will be keeping a count by adding an integer of 1 to the current total.

Increase a Counter

file_type_js app.js
const db = firebase.firestore();
const increment = firebase.firestore.FieldValue.increment(1);

// Document reference
const storyRef = db.collection('stories').doc('hello-world');

// Update read count
storyRef.update({ reads: increment });

Decrease a Counter

We can decrement a counter by simply changing the interval value to -1.

file_type_js app.js
const decrement = firebase.firestore.FieldValue.increment(-1);

// Update read count
storyRef.update({ reads: decrement });

Update a Floating Point Total

It also works with decimal values, which can be useful when updating something like a user’s order history total.

const userRef = db.collection('users').doc('some-user');
const increaseBy = firebase.firestore.FieldValue.increment(23.99);

userRef.update({ totalSpent: increaseBy });

Counting Values in Separate Documents

In certain cases, your counter value may depend on the creation of a new document or just be located in a different document. You can use batched writes to ensure all data is updated safely. For example, we might want to create a like document, then update the total multiple places.

const batch = db.batch();
batch.set(likesRef, ...likeData);
batch.update(storyRef, { likes: increment });
batch.update(userRef, { totalLikes: increment });
batch.commit();

Keep a Count of Documents in a Collection

Keeping a count of documents in a collection is perhaps the single most common form of data aggregation in Firestore. Prior to increment, we had the option to (a) read ALL documents clientside or (b) use a Cloud Function to keep a count of documents in a collection.

Now, we have a third option. Create a document called --stats-- for the purpose of maintaining aggregation or metadata about this collection. In your frontend code, use a batch to increment the stats when creating a new document.

const storyRef = db.collection('stories').doc('hello-world');
const statsRef = db.collection('stories').doc('--stats--');

const increment = firebase.firestore.FieldValue.increment(1);

const batch = db.batch();
const storyRef = db.collection('stories').doc(`${Math.random()}`);
batch.set(storyRef, { title: 'New Story!' });
batch.set(statsRef, { storyCount: increment }, { merge: true });
batch.commit();

Questions? Let's chat

Open Discord