How to add changelogs aka audit logs to any Cloud Firestore document.
If you ever built applications with multiple user roles and permissions, where the major part of your application was about settings and configurations, chances are that you might have already implemented or worked on a feature request to implement a changelog of all the changes that are happening in your app, answering the most important bits, what changed, when the changes took place and who made those changes. And even otherwise, for any application that stores and modifies data over time (which, by the way is almost every application with user interaction), it's important to store the logs about what changes are happening in your data. These logs could be used in future for all sorts of audits and bookkeeping purposes and even for data rollbacks, log dashboards, etc.
Storing a change snapshot for all atomic changes to the database records, is important for all applications.
Though, implementing these logs in traditional SQL databases are not that straightforward, if you happen to use any NoSQL databases, chances are that you can store data snapshots for your before and after changes to your data, without breaking a sweat, and thus implementing the very rudimentary changelog of your database. As mentioned earlier, there are 3 important pieces of information, what, when and who, that we are interested in whenever a change occurs. Let's dig a bit deeper into how we are going to proceed in order to make these information available.
What – To be able to provide information about what changes occurred in our data, we need to store snapshots of before and after values with every change that occur for a record aka document (for most common cloud NoSQL solutions, we call a data record as document) in our table aka collection.
When – This information is relatively easier to get, as all we need is the timestamp when the change occurred.
Who – Now comes the tricky part, as, this information is something that's not completely identifiable by the atomic database operations. We need the application to pass in this information whenever an update operation is performed. As a good practice, we would always include the updatedBy field that would contain the userId of the user who requested to perform the update operation to the document.
With above information in place, let's find out how we would implement an audit log for our Cloud Firestore documents.
When the function is invoked due to an update to the document, it contains a snapshot of before and after changes to the document.
Steps
Create a generic createDocument function to create a document in cloud firestore. Note: this step is optional and you can use the default way of creating new documents, however I prefer this cleaner abstraction.
2. Make sure to always include the updatedBy field with the value of the userId of the user requesting the change in your application. This information needs to be added in application level code and would differ depending upon where you are updating the document from. If you use the firebase web sdk, you can write a wrapper updateDocument function to always add this information when updating your document.
3. Create a generic function to add changelog to any document. This will make use of the fact that in cloud firestore you can create a subcollection for each of your cloud firestore document.
4. Create the cloud functions trigger for the document path where you want to add this changelog.
5. And finally, deploy the cloud functions by running firebase deploy --only functions from the command-line.
Congratulations! You have successfully added a changelog to your document which you can verify from the firebase database console.
Now whenever you make an update to your specified document, you'd see that a timed changelog is added to the document within the changelog subcollection. This subcollection could then be used for any sort of bookkeeping or feature implementation in the future.
If you happen to run into issues or spot any mistakes with this article, please feel free to comment and I'd try my best to help you out / correct the mistakes.