Event driven updates with Firebase Functions triggered from Firestore updates

Christian Kaatz
ITNEXT
Published in
2 min readNov 4, 2020

--

Image a typical scenario, where a User would initiate an Action from the Frontend which would trigger an update on an administrative data set. Of course, you could do the update straight ahead within the operation but could lead to longer operation time or even cause errors.

It should be a priority, to keep the UI/UX as smooth and fast as possible, therefore it is better to make only the least necessary action for the user and do any other tasks asynchronously.

Some examples for such administrative tasks could be the recalculation of a rank of a given item, or an NPS score.

Frontend Code — React or any other Framework

In this Scenario, we will focus on the NPS example.

import app from "firebase/app";
import "firebase/firestore";
class Firebase {
constructor() {
app.initializeApp(config);
this.store = app.firestore();
}
trackNPSVote = async ({ score, comment = "" }) => {
const NPSRef = this.store.collection("NPS");
let autoID = await NPSRef.doc().id;
const data = {
score,
createdAt: new Date().toISOString(),
comment,
};
return await NPSRef.doc(`${autoID}`).set(data);
};
}
export default Firebase;

Of course, something needs to call this code, imagine a simple UI with buttons from 1–10 and an optional comment field, on submit, call the trackNPSVote function.

Backend Code — Cloud Functions

Now, we need to listen for new documents (NPS entries) created by users in our Cloud functions

const functions = require("firebase-functions");
function calculateNPS(scores) {
var promoters = 0;
var detractors = 0;
for (var i = 0, l = scores.length; i < l; i++) {
if (scores[i] >= 9) promoters++;
if (scores[i] <= 6) detractors++;
}
return Math.round((promoters / l - detractors / l) * 100);
}
exports.onNPSQuestionnaireFilled = functions.firestore
.document("NPS/{npsEntry}")
.onCreate(async (snap) => {
const questionnaire = snap.data();
const npsRef = firestore.collection("adminCollection").doc("NPS");
const doc = await npsRef.get();
try {
// we already have some historic data
if (doc.exists) {
const npsStats = doc.data();
npsStats.scores.push(questionnaire.score);
npsStats.score = calculateNPS(npsStats.scores);
return await npsRef.set(npsStats);
} else {
// we need to create the document
const score = calculateNPS([questionnaire.score]);
const data = {
score: score,
scores: [questionnaire.score],
};
await npsRef.set(data);
}
return true;
} catch (err) {
console.error(err.message);
}
});

Now, we can calculate our NPS or any other arbitrary KPI without any interruptions for our Users.

--

--

Dedicated Solution Engineer with focus on agile software development, team culture and evolutionary architectures.