2

I have an API when triggered it sent 2 HTTP Get Requests

I want to get the response from the 1st Get Request and store it in a variable

so I can send it with the response of the 2nd Get Request. What is the best way to achieve it

Here is what I tried to do

    dummy.get("/api/dummy/memberProfileStats", isAuthenticated, (req, res) => {
    const userId = res.locals.user
    const memberProfileStatsURL = `/api/reports/users/${userId}/general`
    const memberLastLoginURL = `/api/users/${userId}/lastlogin`
    getRequest(memberLastLoginURL)
        .then(response => {
            let lastLoginTime = response.data.result
        })
        .catch(errorMessage => {
            console.log( 'Member Last Login API ERROR: ' + errorMessage)
            res.json(errorMessage)
        });
    getRequest(memberProfileStatsURL)
        .then(response => {
            let stats = response.data.result
            let pointsRank = [150, 500, 1000, 2000, 3500, 5000, 5500]
            let totalPoints = stats.totalPoints
            res.json({
                data: {
                    totalPoints: stats.totalPoints,
                    totalPointsRedeemed: stats.totalPointsRedeemed,
                    availablePoints: (stats.totalPoints - stats.totalPointsRedeemed),
                    totalSessionTime: secondsToHm(stats.totalSessionTime), //convert sessionTime seconds to hours
                    loginsCount: stats.totalSessions,
                    rank: rank(totalPoints, pointsRank),
                    createdTime: stats.created,
                    lastLoginTime: lastLoginTime,
                },
                result: response.data.httpStatusCode
            })
        })
        .catch(errorMessage => {
            res.json(errorMessage)
        });
})

But i get lastLoginTime is not defined

mohamed adel
  • 695
  • 1
  • 15
  • 36
  • 1
    At least related: https://stackoverflow.com/questions/500431/what-is-the-scope-of-variables-in-javascript?r=SearchResults&s=1|1100.2601 – T.J. Crowder Jan 09 '20 at 12:10
  • 1
    https://codetower.github.io/es6-features/#Let - Read this reference, And you have to handle asynchronous properly – Jeba Jan 09 '20 at 12:12

3 Answers3

4

If you want the request to run in parallel, you can start them both and then wait for both of them with Promise.all. Then, when both are done, use the results from both of them to send the response:

dummy.get("/api/dummy/memberProfileStats", isAuthenticated, (req, res) => {
    const userId = res.locals.user
    const memberProfileStatsURL = `/api/reports/users/${userId}/general`
    const memberLastLoginURL = `/api/users/${userId}/lastlogin`
    // *** Start the requests in parallel
    Promise.all([
        getRequest(memberLastLoginURL)
            .then(response => {
                return response.data.result
            }),
        getRequest(memberProfileStatsURL)
            .then(response => {
                return response.data.result // *** If this is a common thing, consider a wrapper
            })                              //     function for `getRequest` so we don't have to
    ])                                      //     re-write this fulfillment handler repeatedly
    .then(([lastLoginTime, memberStats]) => {
        // *** Both are done, send response
        let pointsRank = [150, 500, 1000, 2000, 3500, 5000, 5500]
        let totalPoints = memberStats.totalPoints
        res.json({
            data: {
                totalPoints: memberStats.totalPoints,
                totalPointsRedeemed: memberStats.totalPointsRedeemed,
                availablePoints: (memberStats.totalPoints - memberStats.totalPointsRedeemed),
                totalSessionTime: secondsToHm(memberStats.totalSessionTime), //convert sessionTime seconds to hours
                loginsCount: memberStats.totalSessions,
                rank: rank(totalPoints, pointsRank),
                createdTime: memberStats.created,
                lastLoginTime: lastLoginTime,
            },
            result: 200 // *** Presumably this is 200 (or 200 is close enough), since this is a successful response
        })
    })
    .catch(errorMessage => {
        console.log( 'You'll want to update this error message: ' + errorMessage)
        res.json(errorMessage)
    })
})

Note that Promise.all's promise gets fulfilled with an array of the results in the same order as the promises fed into it.


Note that in the above it's sending 200, specifically, instead of response.data.httpStatusCode for the result. But if you really need the response.data.httpStatusCode from the memberProfileStatsURL call, you can pass it along to the final fulfillment handler like this:

dummy.get("/api/dummy/memberProfileStats", isAuthenticated, (req, res) => {
    const userId = res.locals.user
    const memberProfileStatsURL = `/api/reports/users/${userId}/general`
    const memberLastLoginURL = `/api/users/${userId}/lastlogin`
    // *** Start the requests in parallel
    Promise.all([
        getRequest(memberLastLoginURL)
            .then(response => {
                return response.data.result
            }),
        getRequest(memberProfileStatsURL)
            .then(response => {
                return {memberStats: response.data.result, httpStatusCode: response.data.httpStatusCode};
            })
    ])
    .then(([lastLoginTime, {memberStats, httpStatusCode}]) => {
        // *** Both are done, send response
        let pointsRank = [150, 500, 1000, 2000, 3500, 5000, 5500]
        let totalPoints = memberStats.totalPoints
        res.json({
            data: {
                totalPoints: memberStats.totalPoints,
                totalPointsRedeemed: memberStats.totalPointsRedeemed,
                availablePoints: (memberStats.totalPoints - memberStats.totalPointsRedeemed),
                totalSessionTime: secondsToHm(memberStats.totalSessionTime), //convert sessionTime seconds to hours
                loginsCount: memberStats.totalSessions,
                rank: rank(totalPoints, pointsRank),
                createdTime: memberStats.created,
                lastLoginTime: lastLoginTime,
            },
            result: httpStatusCode
        })
    })
    .catch(errorMessage => {
        console.log( 'You'll want to update this error message: ' + errorMessage)
        res.json(errorMessage)
    })
})
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • I get try is expected when i used your code it have syntax error – mohamed adel Jan 09 '20 at 12:20
  • 1
    @mohamedadel - I was missing a closing `)` on the parameter list of the fulfillment (`then`) handler on the `Promise.all` promise, I've fixed it. It also had an extra `[` in it! Sloppy of me... – T.J. Crowder Jan 09 '20 at 12:23
  • sorry but it still have syntax error on `.then(([lastLoginTime, [memberStats]) ` and i can't fix it :D – mohamed adel Jan 09 '20 at 12:26
  • now i get `result: response.data.httpStatusCode` response is not defined :D @T.J. Crowder – mohamed adel Jan 09 '20 at 12:27
  • 1
    @mohamedadel - Ah, I missed you were using that. Basically, just pass it from the relevant fulfillment handler to that handler. But surely the status code is 200? After all, this is a successfully-completed response... – T.J. Crowder Jan 09 '20 at 12:28
  • Perfect . Thanks – mohamed adel Jan 09 '20 at 12:31
  • 1
    @mohamedadel - No worries! I added a version that would use the `httpStatusCode` from the `memberProfileStatsURL` call, just in case you needed it. Happy coding! – T.J. Crowder Jan 09 '20 at 12:33
  • thanks for all of that what if I add another http request like ` getRequest(NewURL) .then(response => { return response.data.result }),` how can i do it? – mohamed adel Jan 09 '20 at 12:37
  • 1
    @mohamedadel - You follow the same pattern (if you want it in parallel with the others), add it as a third entry in the array passed to `Promise.all`, and then add a third entry to the array destructuring in the fulfillment handler on the promise. See the MDN link above for more on `Promise.all`. If you wanted the request to wait and be done *after* one of the others, you'd put it in the fulfillment handler for the request you want it to wait for. [This answer](https://stackoverflow.com/a/43766002/157247) may also help. – T.J. Crowder Jan 09 '20 at 12:40
  • Pefect Thanks Very Much – mohamed adel Jan 09 '20 at 12:45
1

You are trying to access lastLoginTime - the scope of which is accessible only inside your first getRequest.

Since you want to use the result of your first request in your second getRequest you should do it once you get the successful response from your first getRequesy. The correct way to do this in your code is

dummy.get("/api/dummy/memberProfileStats", isAuthenticated, (req, res) => {
    const userId = res.locals.user
    const memberProfileStatsURL = `/api/reports/users/${userId}/general`
    const memberLastLoginURL = `/api/users/${userId}/lastlogin`
    getRequest(memberLastLoginURL)
        .then(response => {
            let lastLoginTime = response.data.result
            getRequest(memberProfileStatsURL)
            .then(response => {
                let stats = response.data.result
                let pointsRank = [150, 500, 1000, 2000, 3500, 5000, 5500]
                let totalPoints = stats.totalPoints
                res.json({
                    data: {
                        totalPoints: stats.totalPoints,
                        totalPointsRedeemed: stats.totalPointsRedeemed,
                        availablePoints: (stats.totalPoints - stats.totalPointsRedeemed),
                        totalSessionTime: secondsToHm(stats.totalSessionTime), //convert sessionTime seconds to hours
                        loginsCount: stats.totalSessions,
                        rank: rank(totalPoints, pointsRank),
                        createdTime: stats.created,
                        lastLoginTime: lastLoginTime,
                    },
                    result: response.data.httpStatusCode
                })
            })
            .catch(errorMessage => {
                res.json(errorMessage)
            });
        })
        .catch(errorMessage => {
            console.log( 'Member Last Login API ERROR: ' + errorMessage)
            res.json(errorMessage)
        });
})
GRS
  • 1,829
  • 1
  • 9
  • 23
  • Why? What did you change? How does the change help? – T.J. Crowder Jan 09 '20 at 12:13
  • @T.J.Crowder Your solution is what i tried to answer. But i did not use promise.all Instead what i thought is he can use the response of the first request atleast even though his second getRequest fails. But downvoting for that is really sad :( – GRS Jan 09 '20 at 12:20
1

First of all you are creating your variable inside the promise so the other promise can't read the value. Second you need to wait to the response.

dummy.get("/api/dummy/memberProfileStats", isAuthenticated, async (req, res) => {
    const userId = res.locals.user
    const memberProfileStatsURL = `/api/reports/users/${userId}/general`
    const memberLastLoginURL = `/api/users/${userId}/lastlogin`
    let lastLoginTime = await getRequest(memberLastLoginURL)
        .then(response => {
            return response.data.result
        })
        .catch(errorMessage => {
            console.log( 'Member Last Login API ERROR: ' + errorMessage)
            res.json(errorMessage)
        });
    getRequest(memberProfileStatsURL)
        .then(response => {
            let stats = response.data.result
            let pointsRank = [150, 500, 1000, 2000, 3500, 5000, 5500]
            let totalPoints = stats.totalPoints
            res.json({
                data: {
                    totalPoints: stats.totalPoints,
                    totalPointsRedeemed: stats.totalPointsRedeemed,
                    availablePoints: (stats.totalPoints - stats.totalPointsRedeemed),
                    totalSessionTime: secondsToHm(stats.totalSessionTime), //convert sessionTime seconds to hours
                    loginsCount: stats.totalSessions,
                    rank: rank(totalPoints, pointsRank),
                    createdTime: stats.created,
                    lastLoginTime: lastLoginTime,
                },
                result: response.data.httpStatusCode
            })
        })
        .catch(errorMessage => {
            res.json(errorMessage)
        });
})