node.jsreactjsexpressasynchronousibm-watson

Wait for data from external API before making POST request


I'm using the IBM Watson Tone Analyser API with Express.js and React. I have this code which sends some test to the Watson API:


    // tone-analyser.js
    class ToneAnalysis {
      constructor() {
        const params = {
          username: process.env.USERNAME,
          password: process.env.PASSWORD,
          version_date: '2018-01-31'
        }
       this.Analyzer = new ToneAnalyzerV3(params);
      }
      ToneAnalyser(input) {
        let tones = this.Analyzer.tone(input, (err, tone) => {
          if (err) console.log(err.message)
          let voiceTone = tone.document_tone.tones[0].tone_id;
          console.log(voiceTone) // Logs the right value on Node.js console
          return voiceTone;
        });
        return tones;
     }
    }
    module.exports = ToneAnalysis;  


I then use this on my Express backend like so:


    // server.js
    const ToneAnalysis = require('./api/tone-analyser');
    const app = express();
    const input = {
        tone_input: 'I am happy',
        content_type: 'text/plain'
    }
    app.get('/api/tone', (req, res) => {
        let tone = new ToneAnalysis().ToneAnalyser(input);
        return res.send({
            tone: tone
        });
    });

And I make an API call from React here:


    // App.js
    componentDidMount() {
        this.callApi()
          .then(res => {
            console.log(res.tone); // Logs the wrong value on Chrome console
          })
          .catch(err => console.log(err));
      }

      callApi = async () => {
        const response = await fetch('/api/tone');
        const body = await response.json();

        if (response.status !== 200) throw new Error(body.message);
        console.log(body);
        return body;
      };

I expect the value of res.tone to be a string showing the tone gotten from the tone analysis function (new ToneAnalysis().ToneAnalyser(input);). Instead, I get


    {
      uri: {...},method: "POST", headers: {...}}
       headers: {...},
       uri: {...},
       __proto__: Object
    }

I think this happens because the res.send(...) runs before tone has a value from the API. My question is, how do I make res.send(...) run only after tone has a value?

I tried wrapping the callback function in this.Analyzer.tone(input, [callback]) in an async/await block, but that did not fix the issue. Any ideas on how to fix this will be highly appreciated. Thanks!


Solution

  • If the call to

    let tone = new ToneAnalysis().ToneAnalyser(input); 
    

    returns a promise then you could do something like

    tone.then(res.send.bind(res))