Home > database >  Returning value or object from Firebase cloud function is always null
Returning value or object from Firebase cloud function is always null

Time:11-25

I'm using the below code to return an object from Firebase, but the value is always null.

This is my Firebase cloud function:

exports.useVoucher = functions.https.onCall((data, context) => {
    const type = data.type
    const voucher_code = data.voucher_code
    const current_time = data.current_time
    console.log("useVoucher type is ", type   " and voucher_code is ", voucher_code)
    return admin.database().ref().child("vouchers").child(voucher_code).once("value").then((snapshot) => {
        if (snapshot.exists()) {
            console.log("useVoucher snapshot is ", snapshot.val());
            if (snapshot.val()[type] === true) {
                let path = "vouchers/"   voucher_code   "/"   type
                admin.database().ref().update({
                    [[path]]: current_time
                }, error => {
                    if (error) {
                        console.log("useVoucher failed")
                        return {is_valid: false}
                    } else {
                        console.log("useVoucher succeeded")
                        return {is_valid: true}
                    }
                })
            } else {
                console.log("useVoucher voucher found but type not found or not true")
                        return {is_valid: false}
            }
        } else {
            console.log("useVoucher voucher_code is not valid");
                        return {is_valid: false}
        }
    })
})

And this is how I call it client side (I removed some irrelevant code):

async function testVoucher(type) {
    const voucher_code = input_text.value.trim()
    console.log("submitVoucher voucher_code is ", voucher_code   " type is ", type)
    let current_time = Number(new Date())
    console.log("submitVoucher current_time is ", current_time)
    await useVoucher({ type: type, voucher_code: voucher_code, current_time: current_time })
        .then((res) => {
            console.log("submitVoucher res is ", res)
            let data = res.data
            console.log("submitVoucher data is ", data)
        })
        .catch((error) => {
            console.log("submitVoucher error is ", error)
        })
}

Regardless of which path the cloud function takes, it always returns "res is null." Why is that?

CodePudding user response:

You can try by wrapping your cloud function in promise to return callback of resolve and reject when your execution gets completed.

exports.useVoucher = functions.https.onCall((data, context) => {
    const type = data.type
    const voucher_code = data.voucher_code
    const current_time = data.current_time
    console.log("useVoucher type is ", type   " and voucher_code is ", voucher_code)
    return new Promise(function (resolve, reject) {
        admin.database().ref().child("vouchers").child(voucher_code).once("value").then((snapshot) => {
            if (snapshot.exists()) {
                console.log("useVoucher snapshot is ", snapshot.val());
                if (snapshot.val()[type] === true) {
                    let path = "vouchers/"   voucher_code   "/"   type
                    admin.database().ref().update({
                        [[path]]: current_time
                    }, error => {
                        if (error) {
                            console.log("useVoucher failed")
                            reject({ is_valid: false })
                        } else {
                            console.log("useVoucher succeeded")
                            resolve({ is_valid: true })
                        }
                    })
                } else {
                    console.log("useVoucher voucher found but type not found or not true")
                    reject({ is_valid: false })
                }
            } else {
                console.log("useVoucher voucher_code is not valid");
                reject({ is_valid: false })
            }
        })
    }
    
})

CodePudding user response:

There are two potential reasons for your problem:

  1. You don't chain the Promises. More precisely you don't return the Promise returned by admin.database().ref().update(...);. You should have a return in front of this line.
  2. In case the update is successful you actually don't return anything. As a matter of fact, even by adding return in front of this line you would only return something in case of error and nothing in case of success: admin.database().ref().update({...}, error => { **You only return here**}).

Since you use several if blocks, it is easier to use async/await. The following should do the trick (untested):

exports.useVoucher = functions.https.onCall(async (data, context) => {   // See async keyword

    try {
        const type = data.type
        const voucher_code = data.voucher_code
        const current_time = data.current_time
        console.log("useVoucher type is ", type   " and voucher_code is ", voucher_code)

        const snapshot = await admin.database().ref().child("vouchers").child(voucher_code).get();
        if (snapshot.exists()) {
            console.log("useVoucher snapshot is ", snapshot.val());
            if (snapshot.val()[type] === true) {
                let path = "vouchers/"   voucher_code   "/"   type
                await admin.database().ref().update({
                    [[path]]: current_time
                });
                return { is_valid: true }
            } else {
                console.log("useVoucher voucher found but type not found or not true")
                return { is_valid: false }
            }
        } else {
            console.log("useVoucher voucher_code is not valid");
            return { is_valid: false }
        }
    } catch (error) {
        console.log(error);
        // Important: Look in the doc how to deal with an error in a Callable Cloud Function.
        //https://firebase.google.com/docs/functions/callable#handle_errors
    }

});

Note that we use the get() method. once("value") is correct but using get() is clearer and more readable (get() has also some other advantages for the Client SDK, see the doc).

  • Related