Jacob Jacob - 3 months ago 75
Javascript Question

Firebase .orderByChild().equalTo().once().then() Promises when child doesn't exist

My API

/auth/login
endpoint takes a
req.body
like this:

{
"email": "jacob@gmail.com",
"password": "supersecretpassword"
}


At the endpoint, I make a reference to my Firebase database (
https://jacob.firebaseio.com/users
). I search through the data, and when I find one user with the email that patches
req.body.email
, I compare the password with the one stored in the database.

I followed the promise structure outlined in this Firebase blog post.

router.post('/login', function(req, res) {
const ref = db.ref('/users');

ref.orderByChild('email')
.equalTo(req.body.email)
.once('child_added')
.then(function (snapshot) {
return snapshot.val();
})
.then(function (usr) {

// Do my thing, throw any errors that come up

})
.catch(function (err) {

// Handle my errors with grace

return;
});
});


If no child is found at
ref
, the function does not proceed (see this answer). I don't even get an error thrown.

My goal is to run code when no user is found with a certain email (i.e. no child is found at
ref
that satisfies
.equalTo(req.body.email)
), but not run the code if a user is found. No error is thrown when nothing is found.

I tried adding
return
statements at strategic points in the call to the database (at the end of my
.then()
promises) with the intention of breaking out of the endpoint entirely after the code was run. I then put code after the call to the database:

.then(function (usr) {

// Do my thing, throw any errors that come up

})
.catch(function (err) {

// Handle my errors with grace

return;
});

res.status(401)
.json({
error: 'No user found',
)};

return;
});


but this code is run whether the call to the database succeeds or not, since the call is asynchronous.

How do I react to this call to the database if it doesn't return anything and still use Firebase promises?

Answer

The child_added event only fires if the query matches at least one of the keys under users and will fire multiple times if there are multiple matches.

You can use the value event instead - it will fire only once and its snapshot will contain all of the keys under users that matched or will have value of null if there are no matches:

router.post('/login', function(req, res) {
  const ref = db.ref('/users');

  ref.orderByChild('email')
    .equalTo(req.body.email)
    .once('value')
    .then(function (snapshot) {
      var value = snapshot.val();
      if (value) {
        // value is an object containing one or more of the users that matched your email query
        // choose a user and do something with it
      } else {
        res.status(401)
          .json({
            error: 'No user found',
          )};
      }
    });
});

Regarding handling of errors, you can wire up the promise and express error handling like this:

router.post('/login', function(req, res, next) {
  const ref = db.ref('/users');

  ref.orderByChild('email')
    .equalTo(req.body.email)
    .once('value')
    .then(function (snapshot) {
      ...
    })
    .catch(next);
});
Comments