I'm using a callable function from a client app to retrieve some data from firestore. The data is created using:
projectData = {
created: firebase.firestore.FieldValue.serverTimestamp(),
...otherData
}
firebase.firestore().collection(projectsCollection).add(projectData)
And I can see the timestamp is correctly saved in the firestore console. The callable function does some other things and has error handling, but the data retrieval is done like this (using lodash to expand each document into an object to return to the client):
const projectRef = firestore.collection(gProjectsCollection).orderBy('created')
return projectRef.get().then(snapshot => {
return {
projects: _.chain(snapshot.docs)
.keyBy('id')
.mapValues(s => s.data())
.value()
}
})
This mostly works, but the returned object has a mangled created
property (shown here in functions shell output):
RESPONSE RECEIVED FROM FUNCTION: 200, {
"result": {
"projects": {
"XcRyQyaxLguRdbNmxQdn": {
"name": "c",
"description": "c",
"created": {
"_seconds": 1543405587,
"_nanoseconds": 169000000
}
}
}
}
}
I'm presuming this is because the callable function uses JSON.stringify()
internally, and the server timestamp isn't converted correctly. I tried explicitly converting the timestamp to a date like this:
return projectRef.get().then(snapshot => {
return {
exVersion,
exId,
projects: _.chain(snapshot.docs)
.keyBy('id')
.mapValues(s => s.data())
.forEach(d => { d.created = d.created.toDate() })
.value()
}
})
but now I get an empty object back:
RESPONSE RECEIVED FROM FUNCTION: 200, {
"result": {
"projects": {
"XcRyQyaxLguRdbNmxQdn": {
"name": "c",
"description": "c",
"created": {}
}
}
}
}
I suspect the real problem here is that callable functions aren't set up to return date objects. If I go one more step and convert the timestamp into a string, I finally get something back in the client:
return projectRef.get().then(snapshot => {
return {
exVersion,
exId,
projects: _.chain(snapshot.docs)
.keyBy('id')
.mapValues(s => s.data())
.forEach(d => { d.created = d.created.toDate().toISOString() })
.value()
}
})
gives:
RESPONSE RECEIVED FROM FUNCTION: 200, {
"result": {
"exVersion": 9,
"exId": null,
"projects": {
"XcRyQyaxLguRdbNmxQdn": {
"name": "c",
"description": "c",
"created": "2018-11-28T11:46:27.169Z"
}
}
}
}
I couldn't find anything in the documentation for callable functions about special handling for dates, but am I missing something in how this should be done?
Some further analysis in the callable function suggests JSON.stringify()
is involved, but isn't the whole problem:
console.log(JSON.stringify(d.created)):
info: {"_seconds":1543405587,"_nanoseconds":169000000}
JSON.stringify(d.created.toDate())
into: "2018-11-28T11:46:27.169Z"
So calling d.created.toDate()
should be sufficient. But I've had other cases where Date
objects are just not returned by callable functions, e.g.:
const testObject = {
some: 'thing',
d: new Date()
}
console.log('JSON:', JSON.stringify(testObject))
return projectRef.get().then(snapshot => {
return {
testObject,
projects: _.chain(snapshot.docs)
.keyBy('id')
.mapValues(s => s.data())
.forEach(d => { console.log('d.created is:', JSON.stringify(d.created.toDate())); d.created = d.created.toDate() })
.value()
}
})
Note in the output below, the logged results of JSON.stringify()
on date objects seems to work, but when those objects are contained within the object returned from the function, they both come out as empty objects:
firebase > getAllProjects({uid: 1234})
Sent request to function.
firebase > info: User function triggered, starting execution
info: JSON: {"some":"thing","d":"2018-11-28T13:22:36.841Z"}
info: Got 1 projects
info: d.created is: "2018-11-28T11:46:27.169Z"
info: Execution took 927 ms, user function completed successfully
RESPONSE RECEIVED FROM FUNCTION: 200, {
"result": {
"testObject": {
"some": "thing",
"d": {}
},
"projects": {
"XcRyQyaxLguRdbNmxQdn": {
"description": "c",
"created": {},
"name": "c"
}
}
}
}