javascriptrestbotkit

How to store and access conversation variables from external API request in BotKit?


Summary

I'm making a web app chatbot with BotKit (using the BotkitConversation API). I want to query an external API using input from the user, and then return some of the resulting data to the user. I am able to store the user's input as a variable on the dialog object (using the {{vars.key}} template) and I can access the JSON response from the API and display in the console, but not in the chat window.

What I've tried

I am trying to use convo.setVar() to store a bit of the JSON data. I know the API call worked because I also console.log that same value and it is as expected.

some code

(note: things like api_key and endpoint are stored elsewhere, this is just an excerpt)

/* The Query Dialog */
let DIALOG_ID = 'my_dialog_1';
let query_dialog = new BotkitConversation(DIALOG_ID, controller);
let total = 0;

query_dialog.ask('What search term would you like to search with?', async(queryTerm, query_dialog, bot) => {

    query_dialog.setVar('queryTerm', queryTerm);

    console.log(`user query is "${ queryTerm }".`);
    console.log(`fetching results from: ${endpoint}?api_key=${api_key}&q=${queryTerm}`);

    fetch(`${endpoint}?api_key=${api_key}&q=${queryTerm}`)
        .then((response) => response.json())
        .then((json) => {
            query_dialog.setVar('total', json.total);
            console.log(`~~total number of results: ${json.total}~~`); // this shows: 2
            // bot.say(`there were {{vars.total}} results`); // this never gets called
            // bot.say(`there were ${total} results`); // this never gets called
            total = json.total;
        })
        // .then((json) => console.log(`~~total number of results: ${json.total}~~`)) // this shows: 2
        // .then((json) => query_dialog.setVar('total', json.total), () => (bot.say(`there were {{vars.total}} results`))) // this doesn't run
        .catch(error => console.log(error))
}, 'queryTerm');

query_dialog.say(`user query is {{vars.queryTerm}}.`); // this works as expected
query_dialog.say(`there were {{vars.total}} results`); // shows "" results
// query_dialog.say(`there were ${total} results`); // says "there were 0 results", which is incorrect, and just pulls from whatever `let total` started with

controller.addDialog(query_dialog);
/* End Dialog */

/* Trigger the dialog */
controller.hears('query', 'message', async(bot, message) => {
    await bot.beginDialog(DIALOG_ID);
});

expectation vs. reality

As expected, in the chat window, the user's query is repeated back to them: "user query is {your_query}". However, the next line displayed is "there were results", which means nothing is stored in vars.total. The actual output should be a number.


Solution

  • The issue you are having here is that the call to fetch returns a promise, and the async handler function resolves immediately instead of waiting for fetch to return the results and set them into the dialog memory space.

    The easiest way to resolve this would be to return a promise so you can control when it resolves:

    return new Promise(function(resolve, reject) { 
    
     /// do fetch here
     fetch.then().then().then(resolve)
    
    });