8

I'm using admin.auth().getUser(uid) in the Firebase Admin SDK (NodeJS), in a Serverless project, and it does successfully return a result. But even though my function returns the result, my lambda still doesn't terminate and I have to use CTRL+C to end it.

Here's the full code of my function (in TypeScript):

public getUser(uid: string): any {
  console.log('FirebaseManager getUser method start');
  const self: FirebaseManager = this;
  const promise: any = self.getDeferred();

  admin.auth().getUser(uid)
  .then(function(userRecord: admin.auth.UserRecord) {
    console.log("Successfully fetched user data:", userRecord);
    promise.resolve(userRecord);
  })
  .catch(function(error: FirebaseError) {
    console.log("Error fetching user data:", error.errorInfo);
    promise.reject('Error getting Firebase user ' + uid);
  });

  return promise.promise;
}

Is there something I'm doing wrong?

Note that if I comment out the admin.auth().getUser(uid) block (and replace it with promise.resolve("ok")), my function does terminate properly. I mean, this doesn't hang (but it's a bit useless ^^):

public getUser(uid: string): any {
  console.log('FirebaseManager getUser method start');
  const self: FirebaseManager = this;
  const promise: any = self.getDeferred();

  promise.resolve("ok");

  return promise.promise;
}

I'm using Serverless 1.21.1, Typescript 2.5.2, Node 6.11.3 or 8.4.0 (2 different dev environments, both same result)

user14764
  • 748
  • 1
  • 9
  • 19

1 Answers1

11

Apparently, you need to run admin.app().delete();, otherwise the connection to Firebase remains active, preventing the process from terminating.

So in my example code, considering that I will not need to use Firebase anymore after running this function:

public getUser(uid: string): any {
  console.log('FirebaseManager getUser method start');
  const self: FirebaseManager = this;
  const promise: any = self.getDeferred();

  admin.auth().getUser(uid)
  .then(function(userRecord: admin.auth.UserRecord) {
    console.log("Successfully fetched user data:", userRecord);
    admin.app().delete();
    promise.resolve(userRecord);
  })
  .catch(function(error: FirebaseError) {
    console.log("Error fetching user data:", error.errorInfo);
    admin.app().delete();
    promise.reject('Error getting Firebase user ' + uid);
  });

  return promise.promise;
}
user14764
  • 748
  • 1
  • 9
  • 19
  • 2
    +1. Calling `admin.initializeApp()` starts a background task, which is what's keeping your process alive. Calling `app.delete()` terminates this task, ensures graceful exit. – Hiranya Jayathilaka Sep 13 '17 at 17:35
  • 2
    excellent solution! One way to reduce the repetition of calling `admin.app().delete()` in two places would simply to be adding in a `.then` after the `.catch` and calling `admin.app().delete()` there, since the final `.then` will always run. – Ariel Salem Apr 18 '19 at 16:02
  • 1
    finally! I ran into the same issue when using db Reference objects and knew it had be to be an open connection. I scoured the docs but couldn't find anything about it – Felipe Apr 28 '21 at 16:17
  • 1
    The syntax has changed in firebase-admin version 10+. You'll want to use `deleteApp(auth.app)` rather than `admin.app().delete()` . – Edmund Rosewright Dec 25 '21 at 15:43