Home > OS >  Retrieving Cloud Firestore `timestamp` value after save
Retrieving Cloud Firestore `timestamp` value after save

Time:03-18

Let's say I'm adding some data to Cloud Firestore:

import { collection, addDoc, serverTimestamp } from 'firebase/firestore'; 

const ref = await addDoc(collection(db, 'posts'), {
  createdAt: serverTimestamp(),
  modifiedAt: null,
  title: 'A Title',
});

I have rules enforcing createdAt should equal request.time and never be updated, modifiedAt should be null on create but equal to request.time on update, and title should be a string.

The problem arises when I try to update this document:

import { doc, updateDoc } from 'firebase/firestore';

await updateDoc(ref, {
  createdAt: ???, // what goes here?
  modifiedAt: serverTimestamp(),
  title: 'A Title',
});

I need createdAt to be that initial server timestamp value (as a Date object). I can't just put serverTimestamp() because Firestore will try to update that field to the new request.time value and rule enforcement will fail. I can't do a partial update (by omitting createdAt) because I'm using a client-side ORM (Ember Data), which isn't capable of handling partial updates like this.

Is there a way to retrieve whatever that initial serverTimestamp value became after calling addDoc, without making a second request immediately after addDoc to retrieve the document we just created? It seems unnecessary to make that second request, since I already have all the data (sans the timestamp values). I'm basically just trying to avoid adding a second request app-wide every time a new record is created.

CodePudding user response:

An updateDoc operation (unlike an addDoc operation or the default setDoc operation) will only overwrite the keys that you actually specify. So to leave createdAt as-is, simply omit it.

await updateDoc(ref, {
  modifiedAt: serverTimestamp(),
  title: 'A Title',
});

To pluck the key from your CRM, use delete on the key.

const dataToUpdate = {
  createdAt: "somevalue",
  modifiedAt: serverTimestamp(),
  title: 'A Title'
}
delete dateToUpdate.createdAt;

await updateDoc(ref, dataToUpdate);

Additionally, adding a getDoc immediately after isn't as network expensive as you think it is. If your application uses that document anywhere in your application (either listening to the document itself or its parent collection), your client app will be notified and given the updated data to store in its local cache regardless of if you need it immediately after adding it.

const ref = await addDoc(collection(db, 'posts'), {
  createdAt: serverTimestamp(),
  modifiedAt: null,
  title: 'A Title',
});

const snapshotWithServerTimestamps = await getDoc(ref);
  • Related