1

I have a request function that makes a call to an endpoint and gets data from it. Then i pass this data to an hbs template for rendering but due to asyncronycity the array passed to template is empty. What is the right way to do it. Please help. The code snippet is below.

app.get("/verifier", keycloak.protect('verifier'), (req, res) => {  
  cirRes = cirJSON.parse(cirJSON.stringify(res))
  //the req object is a circular object so you cannot convert it to json normaly wihtout data loss.
  allUser = []
  access_token = cirRes.req.kauth.grant.access_token.token
  var options = { method: 'GET',
  url: 'http://localhost:8080/auth/admin/realms/create_user_realm/users',
  headers: 
   { 
     'Content-Type': 'application/x-www-form-urlencoded',
     Host: 'localhost:8080',
     Accept: '*/*',
     Authorization: `Bearer ${ access_token }` } }
  request(options, function (error, response, body) {
    if (error) throw new Error(error);
      data = JSON.parse(body)
      for (user in data){
       allUser.push(data[user])
      } 
      //console.log(allUser)
  })
  if (res.statusCode != 200) {
    res.redirect('/logout')
  }
  console.log(allUser)
  res.render('verifier', {"users": allUser})
})

The allUser comes out to be an empty array in above code.

edit >>>>

This question was closed given the answer in this thread. How do I return the response from an asynchronous call? It covers different ways to make asynchronous requests.

Going through the same i made this change in code

app.get("/verifier", keycloak.protect('verifier'), (req, res) => {  
  cirRes = cirJSON.parse(cirJSON.stringify(res))
  //the req object is a circular object so you cannot convert it to json normaly wihtout data loss.
  //console.log(cirRes.req.kauth.grant.access_token.token)
  allUser = []
  access_token = cirRes.req.kauth.grant.access_token.token
  var options = { method: 'GET',
  url: 'http://localhost:8080/auth/admin/realms/create_user_realm/users',
  headers: 
   { 
     'Content-Type': 'application/x-www-form-urlencoded',
     Host: 'localhost:8080',
     Accept: '*/*',
     Authorization: `Bearer ${ access_token }` } }
  request(options, function (error, response, body) {
    if (error) { throw new Error(error) }
      data = JSON.parse(body)
      for (user in data){
       allUser.push(data[user])
      } 
      //console.log(allUser)
      res.render('verifier', {"users": allUser})
    })
  if (res.statusCode != 200) {
    res.redirect('/logout')
  }
})

This works fine, but is the correct according to best practices of nodeJS and what is the best practice for this in nodeJS so that some fallback mechanism is there to render even if an error occurs in call.

Rahul
  • 895
  • 1
  • 13
  • 26

2 Answers2

1

Move your res.render() and res.redirect() calls so they are inside the callback function of your request() call. That's how you use asynchronously provided information in express when you want to send it to a user's browser or API client.

O. Jones
  • 103,626
  • 17
  • 118
  • 172
0

It's perfect. you need to add option variable in try-catch block.

try{
var options = { method: 'GET',
  url: 'http://localhost:8080/auth/admin/realms/create_user_realm/users',
  headers: 
   { 
     'Content-Type': 'application/x-www-form-urlencoded',
     Host: 'localhost:8080',
     Accept: '*/*',
     Authorization: `Bearer ${ access_token }` } }
  request(options, function (error, response, body) {
    if (error) { throw new Error(error) }
      data = JSON.parse(body)
      for (user in data){
       allUser.push(data[user])
      } 
      //console.log(allUser)
      res.render('verifier', {"users": allUser})
    })
  if (res.statusCode != 200) {
    res.redirect('/logout')
  }

}catch(e){
  throw(e);
} 
Prabind
  • 21
  • 3