How to get the return value of one node.js method in another nodejs script in Apigee?

brinda
New Member

I have 2 node.js scripts in Apigee Node.js ABC and Node.js DEF. Now ABC is the main script and DEF is an object script. I have a method DEFMethod in DEF which returns the status code. I am trying to call that DEFMethod in ABC and DEFMethod return a value which should be passed to ABC. After ABC gets that value it proceeds accordingly. But I am having issue in passing the value. The check is always undefined.

This is how I call DEFMEthod it in ABC script

ds =new DEF(req, resp);
check = ds.DEFMethod(select_contact,resp);

DEF script

function(error, response, body){if(response.statusCode ==200){//go back 
                console.log("Response:",body);
                console.log("Status Code:",response.statusCode);return resp.sendStatus(200);}else{
                console.log("Response:",body);
                console.log("Status Code:",response.statusCode);return resp.sendStatus(404);}});

Please let me know what the issue is?

Solved Solved
0 6 130K
1 ACCEPTED SOLUTION

I see, I understand now. Yours is a common question with JavaScript.

In nodejs, there is a common pattern, called "Asynchronous callbacks". Or "Asynchronous Javascript". There is a good overview here. Basically the idea is: invoke a function and DON'T examine the immediate return code of that function. Instead, give that function, ANOTHER function to call (passing a status code or other return value) when the first function finishes. The SECOND function is how you get the result of the first function.

This is really common in nodejs modules, and it is the pattern used by the kvm.get() function. Look at it: you pass a key, and a function. The latter is known as a "callback function". The immediate return value of the kvm.get() function is expected to be ignored - the only way information comes out of it, is via the callback function. The request() function you are using, to get a URL... works the same way.

Your DirectorySearch.prototype.callDirectorySearch function wraps both the kvm.get() and the request(). To get what you want, the callDirectorySearch() must ALSO accept a callback function, and you'll use it in the same way you use the cb in the kvm.get or the request().

The code looks like this:

DirectorySearch.prototype.callDirectorySearch = function(select_contact, resp, cb) {
  var kvm = access.getKeyValueMap("Authorization", 'environment');
  kvm.get('ID', function(err, key_value) {
    // this is the anonymous cb function for kvm.get()
    request( {
        url : 'URL', 
        headers : {
          "apikey" : key_value
        }
      },
      function(error, response, body) {
        // this is the anon cb fn for request()
        console.log("Response:", body);
        console.log("Status Code:", response.statusCode);
        // now, invoke the cb fn that was passed to callDirectorySearch
        cb(response.statusCode); 
      });
  });
};

And to invoke it, you would have something like this in your main script:

var DirectorySearch = require('./CallDirectorySearch.js');
var ds = new DirectorySearch(req, resp);
ds.callDirectorySearch(select_contact, resp, function(statusCode) {
  console.log('status code received: ' + statusCode);
});

There may be other optimizations to make in your code. For example I don't see any use of the select_contact or the resp parameters in the callDirectorySearch() function. Why pass them? Also, I don't see any use of this.requestNamespace or this.responseNamespace either. So why have DirectorySearch be a class ? But maybe you are not finished implementing the code yet.

View solution in original post

6 REPLIES 6

Just pointing it out for other readers:

This is a basic nodejs question. Has nothing to do with Apigee's hosting of nodejs.

Here's what you do.

First, your main module. like this

var express = require('express'),
    app = express(),
    port,
    helper = require('./helper.js');


//app.set('json spaces', 2);


function unhandledRequest(req, response, next){
  response.status(400)
    .json({ error: "unhandled request"})
    .end();
}


function requestHandler(request, response, next) {
  var outboundPayload = {
        message: "hello",
        time: (new Date()).toISOString(),
        value: helper.f1()
      };
  response.status(200)
    .json(outboundPayload)
    .end();
}


app.get('/*', requestHandler);


app.use(unhandledRequest);


port = process.env.PORT || 5950;
app.listen(port, function() {
  console.log('Echo Listening on ' + port);
});

Now the helper module. What you're calling "DEF". like this:

function myFunction1(req, resp) {
  return 5;
}


function myFunction2(req, resp) {
  return new Date ();
}


module.exports = {
  f1: myFunction1,
  f2: myFunction2
};

Then, the main module can call functions exported by the helper module.

See the line that calls helper.f1() .

If you want to pass arguments to those functions, go right ahead. They're just functions.

good luck!

Hi Dino,

I am able to call the function from helper module in the main script. But that helper module after processing, returns a status code and I want that status code to be passed to main script. I am having issue with that.

This is what I am doing in main script:

var DirectorySearch = require('./CallDirectorySearch.js');
ds = new DirectorySearch(req, resp); 
check = DirectorySearch.callDirectorySearch(select_contact, resp);

I want check to have the status code so that I can compare the value and process further.

This is my DirectorySearch script

(function() {
    'use strict';
    var http = require('http');
    var request = require('request');
    var access = require('apigee-access');
    var statuscode;
    console.log('Calling DirectorySearch to check the netid');

    function DirectorySearch(requestNamespace, responseNamespace) 
    {
        this.requestNamespace = requestNamespace;
        this.responseNamespace = responseNamespace;
    }

    DirectorySearch.prototype.callDirectorySearch = function(select_contact,resp)
    {
        var kvm = access.getKeyValueMap("Authorization", 'environment');
        kvm.get('ID', function(err, key_value)
        {
            request(
            {
                url : 'URL', 
                headers : 
                {
                    "apikey" : key_value
                }
            },
            function(error, response, body) 
            {
                if (response.statusCode == 200) 
                {
                    //go back 
                    statuscode = 200;
                    console.log("Response:", body);
                    console.log("Status Code:", response.statusCode);
                    return statuscode;
                } 
                else 
                {
                    statuscode = 404;
                    console.log("Response:", body);
                    console.log("Status Code:", response.statusCode);
                    return statuscode;
                }
            });
        });
    };
    module.exports =  {
      callDirectorySearch: callDirectorySearch
    };
}());

I see, I understand now. Yours is a common question with JavaScript.

In nodejs, there is a common pattern, called "Asynchronous callbacks". Or "Asynchronous Javascript". There is a good overview here. Basically the idea is: invoke a function and DON'T examine the immediate return code of that function. Instead, give that function, ANOTHER function to call (passing a status code or other return value) when the first function finishes. The SECOND function is how you get the result of the first function.

This is really common in nodejs modules, and it is the pattern used by the kvm.get() function. Look at it: you pass a key, and a function. The latter is known as a "callback function". The immediate return value of the kvm.get() function is expected to be ignored - the only way information comes out of it, is via the callback function. The request() function you are using, to get a URL... works the same way.

Your DirectorySearch.prototype.callDirectorySearch function wraps both the kvm.get() and the request(). To get what you want, the callDirectorySearch() must ALSO accept a callback function, and you'll use it in the same way you use the cb in the kvm.get or the request().

The code looks like this:

DirectorySearch.prototype.callDirectorySearch = function(select_contact, resp, cb) {
  var kvm = access.getKeyValueMap("Authorization", 'environment');
  kvm.get('ID', function(err, key_value) {
    // this is the anonymous cb function for kvm.get()
    request( {
        url : 'URL', 
        headers : {
          "apikey" : key_value
        }
      },
      function(error, response, body) {
        // this is the anon cb fn for request()
        console.log("Response:", body);
        console.log("Status Code:", response.statusCode);
        // now, invoke the cb fn that was passed to callDirectorySearch
        cb(response.statusCode); 
      });
  });
};

And to invoke it, you would have something like this in your main script:

var DirectorySearch = require('./CallDirectorySearch.js');
var ds = new DirectorySearch(req, resp);
ds.callDirectorySearch(select_contact, resp, function(statusCode) {
  console.log('status code received: ' + statusCode);
});

There may be other optimizations to make in your code. For example I don't see any use of the select_contact or the resp parameters in the callDirectorySearch() function. Why pass them? Also, I don't see any use of this.requestNamespace or this.responseNamespace either. So why have DirectorySearch be a class ? But maybe you are not finished implementing the code yet.

It worked. Thank you!!

Hi @Dino,

I got the same problem, I try to make a RETURN but it won't work..

Can you help me?

'use strict';
var express = require('express');
var router = express.Router();

/* GET home page. */

//Function 2: createTag
var createTag = function hi (TanentValue) {
    var https = require('https');

    var data = JSON.stringify({
        name: TanentValue,
        schemaPath: "Tag"
    });
    var options = {
        hostname: 'qlik_dev.mcs.be',
        path: '/meteor/qrs/tag?xrfkey=1234567890123456',
        method: 'POST',
        headers: {
            'x-qlik-xrfkey': '1234567890123456',
            'hdr-usr': 'MCS\\gaka',
            'Content-Type': 'application/json'
        },
    };

    var req = https.request(options, (res) => {
        //console.log(res)

        res.on('data', (d) => {
            console.log("hi tag")
            var getResult = "GaLvAnI"; // ----> return this and use it into the function createStream
            return getResult;
        })
    })
        ;
    req.on('error', (error) => {
        console.error(error)

    });

    req.write(data);
    req.end();
}
//Function 1: createStream
var createStream = function (TanentValue) {
    var https = require('https');
    var galvani = hi(); // --------> here I made a variable to call return value
    var data = JSON.stringify({
        name: TanentValue,
    });

    var options = {
        hostname: 'qlik_dev.mcs.be',
        path: '/meteor/qrs/stream?xrfkey=1234567890123456',
        method: 'POST',
        headers: {
            'x-qlik-xrfkey': '1234567890123456',
            'hdr-usr': 'MCS\\gaka',
            'Content-Type': 'application/json'
        },
    };

    var req = https.request(options, (res) => {

        res.on('data', (d) => {
            console.log(galvani); // -----> use the variable here
        })
    })
        ;
    req.on('error', (error) => {
        console.error(error)
    });

    req.write(data);
    req.end();
}
//homepage
router.get('/', function (req, res) {
    res.render('index', { title: 'MCS Test' });
});
//create
router.post('/create', function (req, res) {
    //create tag
    console.log('POST / Call Create Tag');
    createTag(req.body.TanentValue);
    //create stream
    console.log('POST / Call Create Stream');
    createStream(req.body.TanentValue);

    res.send('Stream and Tag has been created');
});
module.exports = router;

Hi

I don't clearly understand the problem you're having.

Again it seems to be a nodejs problem. It's possible the forums for Apigee Edge are not the best place to get help for your problem. Did you try reading about callbacks?

If yours is an Apigee-related question, please do post your question as a new question, and I'll try to answer there.