I have a firestore collection called Posts I make an insert on the client side and it works.
I want to add the createdAt and updatedAt fields to every insert in my p
This solution supports first-level subcollections and is based off @Jonathan's answer above:
**
* writes fields common to root-level collection records that are generated by the
* admin SDK (backend):
* - createdAt (timestamp)
* - updatedAt (timestamp)
*/
exports.createCommonFields = functions.firestore
.document('{colId}/{docId}')
.onWrite(async (change, context) => {
// the collections you want to trigger
const setCols = ['posts', 'reviews', 'comments', ];
// run the field creator if the document being touched belongs to a registered collection
if (setCols.includes(context.params.colId)) {
console.log(`collection ${context.params.colId} is not registered for this trigger`);
return null;
} else {
console.log(`running createCommonFields() for collection: ${context.params.colId}`);
}
// cause the creation of timestamp fields only
_createCommonFields(change);
});
/**
* createCommonFields' equivalent for sub-collection records
*/
exports.createCommonFieldsSubColl = functions.firestore
.document('{colId}/{colDocId}/{subColId}/{subColDocId}')
.onWrite(async (change, context) => {
console.log(`collection: ${context.params.colId}, subcollection: ${context.params.subColId}`);
// the subcollections of the collections you want to trigger
// triggers for documents like 'posts/postId/versions/versionId, etc
const setCols = {
'posts': ['versions', 'tags', 'links', ],
'reviews': ['authors', 'versions'],
'comments': ['upvotes', 'flags'],
};
// parse the collection and subcollection names of this document
const colId = context.params.colId;
const subColId = context.params.subColId;
// check that the document being triggered belongs to a registered subcollection
// e.g posts/versions; skip the field creation if it's not included
if (setCols[colId] && setCols[colId].includes(subColId)) {
console.log(`running createCommonFieldsSubColl() for this subcollection`);
} else {
console.log(`collection ${context.params.colId}/${context.params.subColId} is not registered for this trigger`);
return null;
}
// cause the creation of timestamp fields
_createCommonFields(change);
});
/**
* performs actual creation of fields that are common to the
* registered collection being written
* @param {QueryDocumentSnapshot} change a snapshot for the collection being written
*/
async function _createCommonFields(change) {
// simplify event types
const createDoc = change.after.exists && !change.before.exists;
const updateDoc = change.before.exists && change.after.exists;
const deleteDoc = change.before.exists && !change.after.exists;
if (deleteDoc) {
return null;
}
// simplify input data
const after = change.after.exists ? change.after.data() : null;
const before = change.before.exists ? change.before.data() : null;
// prevent update loops from triggers
const canUpdate = () => {
// if update trigger
if (before.updatedAt && after.updatedAt) {
if (after.updatedAt._seconds !== before.updatedAt._seconds) {
return false;
}
}
// if create trigger
if (!before.createdAt && after.createdAt) {
return false;
}
return true;
}
const currentTime = admin.firestore.FieldValue.serverTimestamp();
// add createdAt
if (createDoc) {
return change.after.ref.set({
createdAt: currentTime,
updatedAt: currentTime,
}, { merge: true })
.catch((e) => {
console.log(e);
return false;
});
}
// add updatedAt
if (updateDoc && canUpdate()) {
return change.after.ref.set({
updatedAt: currentTime,
}, { merge: true })
.catch((e) => {
console.log(e);
return false;
});
}
return null;
}