Firestore query with subcollection


Is this a limitation to Firebase or am I doing this all wrong? Everything works until I add the db.collection('users').doc(friendId).get()... in the middle of the code. Thanks in advance.

const db = admin.firestore(); const friendRef = db.collection('users').doc(id).collection('friends'); friendsList = []; friendRef.get().then((onSnapshot) => { if (!onSnapshot.empty) { onSnapshot.forEach((friend) => { const friendId = String(friend.data().person_id); db.collection('users').doc(friendId).get().then((result) => { const firstName = String(result.data().name.first); const lastName = String(result.data().name.last); }) const data = { personId: friendId, firstName: firstName, lastName: lastName, } friendsList.push(data); }) res.send(friendsList); } else { res.send({ 'message': 'no friends' }); } }).catch((e) => { res.send({ 'error': e }); })


Data is loaded from Firestore asynchronously. This means that by the time you're sending the response back to the client, the data hasn't loaded from Firestore yet.

The easiest way to see this is with some well placed logging statements:

console.log("Before getting friend"); db.collection('users').doc(friendId).get().then((result) => { console.log("Got friend"); }) console.log("After getting friend");

When you run just this code it'll print:


Before getting friend

After getting friend

Got friend


That is probably not the order you expected the logs to be in. The reason is that the data may take some time to come back from Firestore. So instead of blocking the thread, it continues running the thread and then calls your callback function when the data is available. And that unfortunately means that your res.send(friendsList) ends up sending an empty list back to the client, since the data hasn't loaded yet.

The solution to this is to use a bunch of nested callbacks, to use Promise.all(), or ES6's new async/await keyword. With promises the code looks like this:

const db = admin.firestore(); const friendRef = db.collection('users').doc(id).collection('friends'); friendRef.get().then((onSnapshot) => { var promises = []; onSnapshot.forEach((friend) => { const friendId = String(friend.data().person_id); promises.push(db.collection('users').doc(friendId).get()); }); Promise.all(promises).then((snapshots) => { friendsList = []; snapshots.forEach((result) => { friendsList.push({ personId: result.id, firstName: result.data().name.first, lastName: result.data().name.last, }); }); res.send(friendsList); }); }).catch((e) => { res.send({ 'error': e }); })

So we first build a list of all friend read operations, then once all of those are done, we build the response and send it back.


  • Django inlinemodeladmin extra option not working
  • MySQL query for Intersect-like function
  • Model belongs_to eiher/or more than one models
  • Facebook Graph API: Permissions to Friends Photos
  • friend declaration in protected section
  • JSF Cannot format given Object as a Date
  • 2-table interaction: insert, get result, insert
  • Peer to peer/adaptive payments with paypal [closed]
  • LINQ to Entities does not recognize the method 'System.Collections.Generic.Dictionary`2[System.
  • Get current user from inside the model in Sails
  • Strong vs Weak entities MYSQL
  • Extract All Possible Paths from Expression-Tree and evaluate them to hold TRUE
  • XSLT foreach repeating nodes to flat
  • List images(01.png) and descriptions(01.txt) from directory
  • Retrieve list of sent friend requests from friend_request FQL table
  • Jackson Parser: ignore deserializing for type mismatch
  • How to use remove-erase idiom for removing empty vectors in a vector?
  • Bad request using file_get_contents for PUT request in PHP
  • D3 nodes and links from JSON with nested arrays of children
  • Disable Enter in editText android
  • Repeat a vertical line on every page in Report Builder / SSRS
  • Why is an OPTIONS request sent to the server?
  • Dynamically accessing properties of knockoutjs observable array
  • AT Commands to Send SMS not working in Windows 8.1
  • php design question - will a Helper help here?
  • Windows forms listbox.selecteditem displaying “System.Data.DataRowView” instead of actual value
  • AngularJs get employee from factory
  • Codeigniter doesn't let me update entry, because some fields must be unique
  • embed rChart in Markdown
  • IndexOutOfRangeException on multidimensional array despite using GetLength check
  • Authorize attributes not working in MVC 4
  • apache spark aggregate function using min value
  • Sorting a 2D array using the second column C++
  • How to get NHibernate ISession to cache entity not retrieved by primary key
  • Observable and ngFor in Angular 2
  • How to Embed XSL into XML
  • How can I use `wmic` in a Windows PE script?
  • UserPrincipal.Current returns apppool on IIS
  • Unable to use reactive element in my shiny app
  • Conditional In-Line CSS for IE and Others?