0

I have a list of users in a database. They have initially been given an access_token, when signing up to db, but as this expires I need to create a new access_token through a refresh_token which has also been saved in the db against a user.

I need to make a POST request to an endpoint to access get a new access token.

I am trying to loop over each row in the database and get a new access_token this way. When there is one user it works perfectly, but when there is more than one user in the users array each user is the same, i.e. the last user in the database table. I guess the request isn't finished and it is trying to send a new request or something like that.

Does anyone know how to get around this?

app.get('/get-users', function (req, res) {
    connection.query('SELECT * from users', function(err, rows, fields) {
        if(err) console.log(err);

        for(var i = 0; i < rows.length; i++) {
            name = rows[i].name;
            userId = rows[i].userId;
            console.log(userId);// this gives the correct userid
            var authOptions = {
                url: url,
                headers: {
                    'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64'))
                },
                form: {
                    grant_type: 'refresh_token',
                    refresh_token: refresh_token
                },
                json: true
            };

            request.post(authOptions, function(error, response, body) {
                if (!error && response.statusCode === 200) {
                    var access_token = body.access_token;
                    users.push({name: name, userId: userId, accessToken: access_token});
                    console.log(userId)//gives last entry in db
                }
            });
        setTimeout(function () {
           console.log(users)
         }, 3000)
        }
    });
   res.send('getting here');
})
  • You've asked this question 30min ago and it was closed : http://stackoverflow.com/questions/40884069/make-post-request-in-loop-node/40884140 – xShirase Nov 30 '16 at 10:10
  • @xShirase i know man, sorry! I wanted to get it working with one user first, as I wasn't 100% sure if that was the issue, but now i know it is. As I said though, the issue isn't sending the response to the browser, but getting the right data pushed into the `users` array. Thanks for your help too, i really appreciate it –  Nov 30 '16 at 10:14
  • I'm still not sure what you're trying to do though. you're refreshing all the user tokens in one go, every time? – xShirase Nov 30 '16 at 10:15
  • in any case, get rid of the for loop, make a function that handles the POST requests, res.send when the function is done – xShirase Nov 30 '16 at 10:17
  • @xShirase i have a list of `refresh_tokens` that need to be changed into `access_tokens`. that is actually working. I think the issue must be what u are saying with the function, but I'm not 100% sure how the function will look like and where it will be called/go in the js file –  Nov 30 '16 at 10:19
  • http://stackoverflow.com/questions/32442426/solution-found-node-js-async-parallel-requests-are-running-sequentially this could help you if you want to run all your requests in parallel – xShirase Nov 30 '16 at 10:22

1 Answers1

0

Your problem is this:

    for(var i = 0; i < rows.length; i++) {
        name = rows[i].name;
        userId = rows[i].userId;
        // ...
        request.post(authOptions, function(error, response, body) {
          // you're using name and userId here
        });
        // and also you use users here:
        console.log(users)
    }

Now, the problem is that no callback will get called (even for the first iteration) before all of the loop iterations finish.

So first of all, the console.log(users) will always get the value of users whatever it was before any users.push() took place.

Additionally, for every callback the value of name and userId will be the same - whatever it was after the last iteration of your for loop.

Your name and userId seem to be global variables, or they are defined in some outer scope. What you need to do is to use let to define them locally, so that they get fresh bindings for every loop iteration.

See a simple example to demonstrate what going on here:

for (var i = 0; i < 4; i++) {
  x = 'Number ' + i;
  setTimeout(function () {
    console.log(x);
  }, 500*i);
}

It's not good because x is a global variable. Now with var:

for (var i = 0; i < 4; i++) {
  var x = 'Number ' + i;
  setTimeout(function () {
    console.log(x);
  }, 500*i);
}

Still not good because there is only one bonding for x shared for every iterations. Now with let:

for (var i = 0; i < 4; i++) {
  let x = 'Number ' + i;
  setTimeout(function () {
    console.log(x);
  }, 500*i);
}

It works because every iteration gets a new binding.

Update

Another thing is making sure that the you know when every request.post has ended so that you can send a response at the right time.

You can use async to help you with that. You can use promises and libraries like Bluebird or Q to help you with that. But you can also do a simple trick. If you want to know that you are inside of the last callback on the example that I showed above, you can count the invocations like this:

let current = 0, last = 0;
for (let i = 0; i < 4; i++) {
  let x = 'Number ' + i;
  setTimeout(function () {
    if (++current === last) console.log('Last callback:');
    console.log(x);
  }, 500*i);
  last++;
}

This may not be the best way to handle complex situations but it demonstrates what is going on: the last variable gets incremented for every iteration and the callbacks are only registered at that time. Then the callbacks start firing one after another and you can increment the current variable every time a ne callback is fired and compare if that was the last one or not.

Now, a general advice: Use let (or const) every time instead of var. It's much more intuitive how it works. Of course you can use var if you know what you're doing but when in doubt use let.

rsp
  • 107,747
  • 29
  • 201
  • 177
  • thanks @rsp. so if i use `let` do you think that will work, or do i need to start adding callbacks aswell? –  Nov 30 '16 at 10:29
  • if i use `let` then I need to use `strict mode`, but i need `users` to be a global variable as i use it in two of my express endpoints. make sense? –  Nov 30 '16 at 10:32
  • @phantom Using `let` for `name`, `userId` and possibly `i` is a start. Another problem is the `console.log(users)` and `res.send` which need to take place after all of the callbacks end. – rsp Nov 30 '16 at 10:34
  • 1
    As for the globals, you may be able to use `global.users` or define `users` with `var users` or `let users` outside both of your handlers, like at the beginning of the file. If the handlers are in separate files then you may do it in a common file that is `require`d by both of them. – rsp Nov 30 '16 at 10:36
  • i know, but it seems as when i try and use let for `name`, `userId` and `i` I need to use strict mode which is then causing problems for `user` –  Nov 30 '16 at 10:36
  • i think i needed to define users at the start of the file :-) –  Nov 30 '16 at 10:37
  • this is great! thanks for your help. should i be using callbacks instead though? am i taking a bad approach? –  Nov 30 '16 at 10:38
  • 1
    @phantom I updated my answer with some ideas on how you can make sure that you are in the last callback and you can return the response. The most elegant way would be to use `async` or primises to handle situations like that but you can also count the invocations of callbacks as I showed and use the `if (++current === last)` trick at the end of your callback to run your `console.log(users)`. The timeout will work but if for some reason not all requests finish in those 3 seconds then you will print incomplete data if you're using the timeout. – rsp Nov 30 '16 at 10:51