Getting response with NodeJS request module

Hi everyone,
I just started using the twitch kraken api and I have a few questions. Whenever I attempt to get the json object
there is no response. I am attempting to run this function through amazon aws lambda, and don’t have access to a console. In the code below my callback function will always print out “SUCCESS got streamers ERROR”. I am pretty certain right now the “ERROR” comes from my initial setting of result. How come result does not get changed into the proper json?
BTW I have used postman and it returns the proper thing with the query and param, and headers

function getJSON(callback){
    var result = "ERROR";
    request.get(url(games[0]),function(error,response,body){
        var d = body;
        result = d.streams[0];//.channel.display_name;
        // for(var i = 0; i < limit; i++){
        //     streamers.push(d.streams[i].channel.display_name)
        // }
        streamers.push(result);
    });
    if (streamers.length < 0){
        callback("ERROR");
    }else{
        callback("SUCCESS got streamers " + result);
    }
}
function url(game){
    return {
        url: "https://api.twitch.tv/kraken/streams/",
        qs : {
            'game' : 'overwatch',
            'limit' : 2
        }
        headers: {
            'Client-ID': clientID,
            'Accept': 'application/json',
            'Accept-Charset': 'utf-8',
        },
        json: true
    };
}

The result doesn’t get changed into JSON because no where in your code are you parsing it. What you get back from the request is just a string, to get the JSON what you’re have to do is something along the lines of:

try {
    result = JSON.parse(body);
} catch (err) {
    // if there is an error during parsing, such as the body not being JSON, it'll be caught here,.
}

Thank you for the reply Dist,
Before some refactoring I had the following
var d=JSON.parse(body);
but the result were the same as now with the output being “SUCCESS got streamers ERROR”.
Since your comment I have switched back to that way, because using json:true was not working either.
Could this be because of it trying to execute before getting a response back?

Current code:

function getJSON(callback){
    var result = "ERROR";
    request.get(url(games[0]),function(error,response,body){
        console.log("requested for url: " + url(games[0]));
        var d = JSON.parse(body);
        result = d.streams[0];//.channel.display_name;
        // for(var i = 0; i < limit; i++){
        //     streamers.push(d.streams[i].channel.display_name)
        // }
        streamers.push(result);
    });
    if (streamers.length < 0){
        callback("ERROR");
    }else{
        callback("SUCCESS got streamers " + result);
    }
}

function url(game){
    return {
        url: "https://api.twitch.tv/kraken/streams/",//twitchlimit,
        qs : {
            'game' : 'overwatch',
            'limit' : 2
        },
        headers: {
            'Client-ID': clientID,
            'Accept': 'application/json',
            'Accept-Charset': 'utf-8',
        }
    };
}

Yes. request is an async function so while it is waiting for a response from the server, the rest of your code will keep on executing, in this case your callback.

move this

if (streamers.length < 0){
    callback("ERROR");
}else{
    callback("SUCCESS got streamers " + result);
}

inside of the callback being passed to request.get, this will mean that if-else statement wont get run until request receives the response.

And in your current code, make sure to wrap the JSON.parse() in a try { ... } catch(err) { } because there will be times where the response is malformed, an error, etc… and this can kill a program if you’re not catching it.

1 Like

I have implemented you suggestions and it seems to have worked a bit. I ended up putting the json.parse into a try/catch block, and moved the if/else statements inside the getJSON method. However, now I don’t get any output.

This is how I am invoking the getJSON method:

function handleGameResponse(intent,session,callback){
    //gets the game
    var game = intent.slots.game.value;
    if (!games.includes(game)){
        var speechOutput = "You asked for: " + intent.slots.game.value;
        //var speechOutput = "You asked for: " + games[game] + " That game is currently not an option. These are your current options: " + arrayToString(games)
        var repromptText = "Please ask one from the current options.";
        var header = "Invalid Game";

    }else {
        getJSON(function(data){
            if(data !== "ERROR"){
                var speechOutput = data; //capitalizeFirst(game) + " top three streamers are: " + arrayToString(streamers) + '.';
                var repromptText = "Do you want to hear more about games?";
                var header = capitalizeFirst(game);
            }else{
                var speechOutput = "I'm sorry, something went wrong and I could not get the streamers.";
            }
            //speechOutput = data;
        });
        //speechOutput = games[0] + " games[0], game= " + game; //this executes so the getJSON isn't executing
    }
    var shouldEndSession = false;
    callback(session.attributes,buildSpeechletResponse(header,speechOutput,repromptText,shouldEndSession));
}

Does the above execute the same way,? As in the shouldEndSession and callback execute before the getJSON has time to give a response? I’ve read things about using a .then would that be applicable here

For ref, this is the getJSON method now:

function getJSON(callback){
    var result = "ERROR";
    request.get(url(games[0]),function(error,response,body){
        try{
            var d = JSON.parse(body);
        } catch (err){
            callback("Sorry, something seems to have malfunctioned while getting the streamers");
        }
        result = d.streams[0].channel.display_name;
        // for(var i = 0; i < limit; i++){
        //     streamers.push(d.streams[i].channel.display_name)
        // }
        streamers.push(result);
        if (streamers.length <= 0){
            callback("ERROR");
        }else{
            callback("SUCCESS got streamers " + result);
        }
    });
}

With the way you have it set up at the moment, when handleGameResponse is called and gets to the getJSON part, it will
call getJSON and pass the function you’ve provided as an argument, but still continue on with the var shouldEndSession = false; callback(session.attributes,buildSpeechletResponse(header,speechOutput,repromptText,shouldEndSession)); as none of the code in the function you’ve passed getJSON gets executed until after it receives a response.

Using .then requires a promise library, or modules that have it built in, I would recommend getting a good understanding of the program flow and callbacks first. You also should be careful of where you’re declaring your variables. for example say the getJSON works but the data is an error, then you have the issue that your callback(... function is being passed arguments that haven’t been declared, as only speechOutput has been declared in that part.

What you might find easier to manage is as regardless of success or failure you’re using the same variables speechOutput, repromptText, header etc…, you could initialise them at the start of function, even just var header = '' and then later in the code you can simply do header = "Invalid Game"; and not have to usevar` to declare it each time, and will also ensure that undefined variables aren’t trying to be passed to anything.

Thank you for the help Dist. I do realize that async operations are a weakpoint of mine and am doing this project to get better at it. It’s still in rough stages and unoptimized, I just wanted the functionality to work. I’m going to look into the request-promise npm module, as it can handle this type of thing better than the request module I am using now.

The request module is really powerful. Here’s an example of a function:

const request = require('request');

// Get config

const krakenDefaults = {
		baseUrl: 'https://api.twitch.tv/kraken/',
		headers: {
			Accept: 'application/vnd.twitchtv.v5+json',
			'Client-ID': config.twitch.api.clientID
		},
		json: true
	};

const kraken = request.defaults(krakenDefaults);

function usernameToUser(username, callback) {
	// Caching here

	return kraken({
		url: 'users',
		qs: { login: username },
		callback: (err, { statusCode }, { _total, users }) => {
			if(err || statusCode !== 200 || _total === 0) {
				callback(err, null);
			}
			else {
				callback(null, users[0]);
			}
		}
	});
}

You can find all of the request library options here.

let input = 'alca';
usernameToUser(input, (err, data) => {
	if(err) {
		console.log('ERR', err);
	}
	else {
		console.log(input, 'is ID', data._id); // 'alca is ID 7676884'
	}
});

With request-promise this would look like:

const rp = require('request-promise');

// Get config

const krakenDefaults = {
		baseUrl: 'https://api.twitch.tv/kraken/',
		headers: {
			Accept: 'application/vnd.twitchtv.v5+json',
			'Client-ID': config.twitch.api.clientID
		},
		json: true
	};

const kraken = rp.defaults(krakenDefaults);

function usernameToUser(username) {
	// Caching here

	return kraken({
		url: 'users',
		qs: { login: username }
	})
	.then(({ _total, users }) => {
		if(_total === 0) {
			return null;
		}
		return users[0];
	});
}
let input = 'alca';
usernameToUser(input)
	.then(data => {
		console.log(input, 'is ID', data._id); // 'alca is ID 7676884'
	});

And then there’s options like “simple” and “resolveWithFullResponse” that you might want.

Hey guys I really appreciate everyones help! I finally got this solved :slight_smile: !!!
Implementing the request-promise module and using the .then() helped a lot
I also implemented default values for the responses that get overwritten in the game request.
Code:

function getJSON(callback){
    var options = {
        uri: 'https://api.twitch.tv/kraken/streams/',
        qs: {
            game: 'overwatch',
            limit: 2
        },
        headers: {
            Accept: 'application/vnd.twitchtv.v5+json',
            'Client-ID': clientID
        },
        json: true // Automatically parses the JSON string in the response 
    }
    rp(options)
    .then(function (jsonResponse) {
        var result = jsonResponse.streams[0].channel.display_name;
        streamers.push(result);
        if (streamers.length <= 0){
            callback("ERROR");
        }else{
            callback("SUCCESS got streamers " + result);
        }
    })
    .catch(function (err) { // API call failed...
        callback("I'm sorry, something went wrong when trying to get streamers. " + err);
    });
}

You could also return at rp(options) to return a Promise. You could still use the callback as well, just make sure to return the data in the then but throw in the catch if there’s no callback.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.