3
votes

When I use firebase.firestore() on client-side, the following function works well and managed to find the document:

With firebase.firestore()

function getUserProfile(email, returnDoc) {
    var db = firebase.firestore();
    var docRef = db.collection("Users").doc(email);

    docRef.get().then(function (doc) {
        if (doc.exists) {
            returnDoc(undefined, doc);
        } else {
            returnDoc("User not found.", doc);
        }
    }).catch(function (error) {
        returnDoc("Error getting user.", doc);
    });
};

On index.js

getUserProfile(user.email, function (err, userProfile) {
   if (!err) {
      $scope.firstName = userProfile.get("FirstName");
      $scope.lastName = userProfile.get("LastName");
      $scope.email = userProfile.get("Email");
      $scope.$apply();
   } else {
      alert(err);
    };
});

But when I tried to create another similar function using firebase-admin, the following function can't find the document with the same email argument:

In db.js With admin.firestore()

const admin = require('firebase-admin');  
admin.initializeApp();

let db = admin.firestore();

function getUserData(email, returnDoc) {
    console.log(`imtp-db.getUserData: ${email}`); // email data is correct and exist in database.

    let docRef = db.collection("Users").doc(email);

    return docRef.get().then(function (doc) {
        console.log(`doc.exists: ${doc.exists}`); // doc.exists: false here.
        if (doc.exists) {
            console.log("Document data:", doc.data());

            return returnDoc(undefined, doc);
        } else {
            return returnDoc("User not found.", doc);
        };
    }).catch(function (error) {
        return returnDoc("Error getting user.", doc);
    });
};

exports.getUserData = getUserData;

In Cloud Function:

const functions = require('firebase-functions');
const db = require("./middleware/db.js");

exports.getUserProfile = functions.https.onCall((data, context) => {
    var userProfile = undefined;
    console.log(`data.email = ${data.email}`);
    return db.getUserData(data.email, function (err, userDoc) {
        console.log(`exports.getUserProfile.err = ${err}`);
        if (!err) {
            userProfile = userDoc.data();
            return {
                error: err,
                returnData: userProfile
            };
        } else {
            return {
                error: err,
                returnData: userProfile
            };
        };
    });
});

Everything works well in the above function without errors except that the doc.exists always evaluated to false and always return "User not found.", why? Anything that I've done wrong in the second version of my code? Thanks!

NOTE: I'm running all emulators: firebase emulators:start

2
In which context are you executing this code with the Admin SDK? In your own server? In a cloud Function?Renaud Tarnec
Yes, on localhost in https.onCall cloud function with all emulators started.Antonio Ooi
Can you please modify your question in order to add the entire code of your Cloud Function?Renaud Tarnec
@RenaudTarnec : Done! I also emphasized that the one with Admin SDK was in fact inside another imported module, i.e. db.js. Thanks again!Antonio Ooi
You should try to refactor your code by using Promises, instead of callback functions. In Cloud Functions you need to use Promises to deal with the asynchronous processes.Renaud Tarnec

2 Answers

3
votes

In reference to this post, apparently, the answer is simple: The local Firestore Emulator has NO DATA. This can be easily proven with the following approach based on the exact same code in the question:

Start all emulators except Firestore Emulator:

  1. firebase serve --only hosting
  2. firebase emulators:start --only functions

Now the document is accessible and doc.exists == true:

enter image description here

Without starting the Firestore Emulator, the code will be forced to access the production server with data. Of course, this is not recommended, especially when you're on Blaze Plan. Hence, as given in the above link, we should setup the test data locally before using Firestore Emulator.

As for the reason why the client-side firebase.firestore() was able to access the data, this is because the Firestore Emulator runs on port different than the hosting. So the firebase.firestore() that was being executed in index.js at the web client side was using localhost:5000, bypassing the Firestore Emulator and went straight to the production server. To resolve this issue, this Firebase Documentation could be the solution (but not yet tested):

// Initialize your Web app as described in the Get started for Web
// firebaseApp previously initialized using firebase.initializeApp().
var db = firebaseApp.firestore();
if (location.hostname === "localhost") {
  db.settings({
    host: "localhost:8080",
    ssl: false
  });
}
0
votes

When you use an emulator, Firebase would not fetch from the actual Firestore database. It fetches from the Firestore emulator, which you can access with firebase emulator ui, to manage data there. Firestore emulator is different from your actual firestore database.

if you run firebase emulators:start you can view the url to access the emulator ui enter image description here