2
votes

Im trying to retrieve some data from firestore within a cloud function, but get nothing back. The same query on the client-side gives me the correct results. It's probably something small but I don't see the issue. What am I doing wrong?

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
db.settings({ timestampsInSnapshots: true });

exports.myFunction = functions.https.onCall((data, context) => {
  const info = getInfo();
  //do some stuff with the info
  return info;
}

function getInfo() {
  const query = db
    .collection('info')
    .where('table_nr', '==', 1)
    .where('number', '<=', 25)
    .orderBy('number', 'desc')
    .limit(1);

  const info = query.get().then(snapshot => {
    snapshot.forEach(doc => {
      return doc.data();
    })
  })
  return info;
}

When I make a call to this function I get: "data: null"

let info = functions.httpsCallable('myFunction')

info().then(res => { console.log(res) })

I tried a lot of different options, like when I change the last part to:

const info = query.get().then(snapshot => {
  snapshot.docs;
})

I get an array with 1 object. So I'm sure there is a document in the query with data. The console.log gives me:

{data: Array(1)}
data: Array(1)
0: {_ref: {…}, _fieldsProto: {…}, _serializer: {…}, _validator: {…}, 
_readTime: {…}, …}
length: 1
__proto__: Array(0)
__proto__: Object

And:

return query.get().then(querySnapshot => {
  if (querySnapshot.empty) {
    return { exists: false }
  } else {
    return { exists: true }
  }
})

The console.log:

{data: {…}}
data:
  exists: true
  __proto__: Object
  __proto__: Object

Mabye good to add that I created an (working) index for the query.

1
What do you mean by "I get data: null"? Are you adding logging to your function that you're not showing here? What are you expecting to be the end result of this function? It seems to me that you're likely not working with promises correctly.Doug Stevenson
Hi Doug, thanks for helping me out! I'm calling this cloud function from the client-side like this: let info = functions.httpsCallable( 'myFunction' ) info().then(res => { console.log(res) })Annemieke
So what happens when you add console logging in your function? Are you actually working with the values that you expect? You'll need to debug it.Doug Stevenson
I tried a lot already. First I changed the last part of the getInfo() function to: return query.get().then(querySnapshot => { if (querySnapshot.empty) { return { exists: false } } else { return { exists: true, data: querySnapshot.data() } } }) this returns: {data: {…}} data: exists: true proto: Object proto: ObjectAnnemieke
That's really hard to read in a comment. Could you edit the question to explain in more detail what you're observing?Doug Stevenson

1 Answers

5
votes

In both cases, you're returning a promise for an object that isn't what you really want to send to the client. When you write a callable, you need to return a promise that resolves to the exact JavaScript object that you want to send. You can't just return anything. What you'll have to do is convert that querySnapshot into plain old JavaScript objects that describe what you want the client to know. A querySnapshot object itself is not serializable - it is a complex object that describes many things about the query results.

First define this: What exactly do you want the client to receive? Define what the actual JavaScript object should look like. Now, convert the query results to look like that. At a minimum, you can send the entire set of documents as plain JS objects like this:

return query.get().then(querySnapshot => {
    return querySnapshot.docs.map(doc => doc.data());
})

This will return an array to the client with raw document objects. But it's not clear to me that's what you want to send (since you didn't define your expectations). But it's a start.