EPaz
EPaz

Reputation: 95

Firebase function using Typescript nested promise

I am trying to create an API for my Firebase project using functions. The difficult part is that I am using TypeScript and I keep running into typing problems.

This is my sign up route:

app.post('/signup', (req: Request, res: Response) =>{
  const newUser = {
    email: req.body.email,
    password: req.body.password,
    confirmPassword: req.body.confirmPassword,
    handle: req.body.handle
  }
  // TODO: validate data
  db.doc(`/users/${newUser.handle}`).get()
    .then( doc => {
      // Duplicated handle
      if(doc.exists){
        return res.status(400).json({ handle: 'this handle is already taken'});
      }
      // Valid handle
      else {
        return firebase.auth().createUserWithEmailAndPassword(newUser.email, newUser.password); 
      }
    })
    .then( data => {
      return data.user.getIdToken();
    })
    .then( token => {
      return res.status(201).json({ token });
    })
    .catch( err => {
      console.error(err);
      return res.status(500).json({ error: err.code })
    });
})

Errors:

  1. The doc in the first has the following error

Argument of type '(doc: DocumentSnapshot) => Response | Promise' is not assignable to parameter of type '(value: DocumentSnapshot) => UserCredential | PromiseLike'.

This is because I am returning a response status if the handle already exists, to avoid duplicates. From my understanding, this would go to the catch; and the valid handle would return a Promise that the next then would take. However, this is not working

  1. data.user.getIdToken() says that

Property 'user' does not exist on type 'DocumentSnapshot'. Tried delcaring a const for user before using it, but get the same message.

I have other functions for post and get from firestore working, but can't get the authentication ones to work.

Thanks for the help!

Upvotes: 1

Views: 474

Answers (1)

Renaud Tarnec
Renaud Tarnec

Reputation: 83068

I've not tested your code but both your problems most probably come from the fact that you are calling firebase.auth().createUserWithEmailAndPassword() and getIdToken(), which are methods of the JavaScript SDK.

In a Cloud function, you need to use the Admin SDK, and therefore call the createuser() method.

  db.doc(`/users/${newUser.handle}`).get()
    .then( doc => {
      // Duplicated handle
      if(doc.exists){
        return res.status(400).json({ handle: 'this handle is already taken'});
      }
      // Valid handle
      else {
        return admin.auth().createUser(newUser.email, newUser.password); 
      }
    })
    .then(userRecord => {
      //... see below 
    })

Note that we replace firebase by admin, in order to use the Admin SDK. Don't forget to initialize it with const admin = require('firebase-admin'); admin.initializeApp();. See https://firebase.google.com/docs/functions/get-started?authuser=0#import-the-required-modules-and-initialize-an-app.

Then, you are using getIdToken() which is, again, a method from the JavaScript SDK.

You need to adapt your code depending on what was your exact goal by sending back a token to the front-end.

If you want to login the user in the front-end, just send back the info that the user was successfully created, and, in the front-end, call the signInWithEmailAndPassword() method.


Finally note two other important points:

  1. You should use a Transaction to check that the user doc is not existing
  2. The db variable shall be declared as admin.firestore().

Upvotes: 1

Related Questions