Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase simple query gives error: "Converting circular structure to JSON at JSON.stringify"

I'm trying to loop through a /users path and join it to my auth().listUsers result however this code:

https://github.com/QuantumInformation/svelte-fullstack-starter/blob/master/firebase_specific/functions/src/users.ts#L32

export async function getSomeUsers(amount: number) {
    try {
        const listUsersResult = await admin.auth().listUsers(amount)
        const parsedUsers = listUsersResult.users.map(stripUserSensitiveInfo).map(async user => {
            console.log("try read_______________" + user.uid)
            let userProfileSnapshot = await admin
                .database()
                .ref("users/" + user.uid)
                .once("value")

            console.log("end try read_______________" + user.uid)
            return { ...user, userProfileSnapshot }
        })

        return parsedUsers
    } catch (error) {
        console.error("Error listing users:", error)
        throw new Error("Error users" + error)
    }
}

gives this error

Converting circular structure to JSON at JSON.stringify () at stringify

but this code works fine

export async function getSomeUsers(amount: number) {
    try {
        const listUsersResult = await admin.auth().listUsers(amount)


        const parsedUsers = listUsersResult.users.map(stripUserSensitiveInfo).map( user => {


            return 1
        })


        return parsedUsers
    } catch (error) {
        console.error("Error listing users:", error)
        throw new Error("Error users" + error)
    }
}
like image 415
Nikos Avatar asked Jan 26 '23 07:01

Nikos


2 Answers

The problem is within the embedded async calls in the map operator In order to make it work, you would need to either wait for each iteration, which would also be VERY inefficient. This is because for every additional query the time taken increases Linearly. My proposal is to convert the entire logic in a way that is scalable. This is fortunately the case with your listAllUsers() function that returns all the results on one call

export async function getSomeUsers(amount: number) {
try {
    /**
     * fetch all the users with the provided limit
     */
    const allUsers = await admin.auth().listUsers(amount)
    /**
     * loop through the returned values and create a promise to fetch
     * Each of their document
     */
    const listUsersResult = allUsers.users.map(user => {
        return admin
            .database()
            .ref("users/" + user.uid)
            .once("value")
    })
    /**
     * When all the user documents have been fetched, iterare through them and deduce their values
     */
    const parsedUsers = await Promise.all(listUsersResult).then(docSnashots => {
        return docSnashots.map(snapshot => {
            console.log("end try read_______________" + snapshot.val())
            /**
             * The records need to be matched with the original values
             */
            const originalUserData = allUsers.users.find(u => u.uid === snapshot.key)
            return { ...originalUserData, ...snapshot.val() }
        })
    })
    return parsedUsers

} catch (error) {
    console.error("Error listing users:", error)
    throw new Error("Error users" + error)
}
}

NOTE this method is more compute intensive because of the loops, but more time efficient since every read operation is independent of the other. It can be modified for situations where you're sure the read operations will always take a very short time and (Maybe) would be more resource efficient than the computation of the loops

like image 121
Kisinga Avatar answered Feb 07 '23 17:02

Kisinga


Based on your 2nd code snippet, i think there is an issue in async call. Did you try whether the async inside the map is working as expected ?. Try like this and check.

    let parsedUsers = [];
    const userList = listUsersResult.users.map(stripUserSensitiveInfo);
    for (const user of userList) {
      try {
        let userProfileSnapshot = await admin
            .database()
            .ref("users/" + user.uid)
            .once("value")
        parsedUsers.push({...user, userProfileSnapshot});
      } catch (error) {
        console.error('Error on API', error);
      }
    }
like image 39
Raj Kumar Avatar answered Feb 07 '23 18:02

Raj Kumar