Unable to send response from Javascript callout

Not applicable

I am trying to create a javascript call out which will call a service internally which returns JSON object. I need help in capturing that response and sending it back as response to external clients from Apigee(Here is the requirement I am trying to achieve.. request comes in through Apigee, javascript call out to make a internal service call, get the data and send the response back to request). Any working examples or samples will be of great help.

Thanks,

Pradeep

0 16 2,044
16 REPLIES 16

Please show us what you have so far! 🙂

Hi @Sean Davis thanks for trying to help me out. Here is what I did with my Javascript:

try {
  var productsData = httpClient.get( 'http://xxxx:9400/json/Api/GetDetails');
  context.setVariable('responseawait', productsData);
  var r = context.getVariable('responseawait');
  if (r) {
    r.waitForComplete();
    if (r.isSuccess()) {
      context.setVariable('responseContent', JSON.stringify(r.getResponse().content)); // Is this proper way to set response object?
    }
  }


} catch (err) {


}

For this I am getting response- "error":TypeError: Cannot read property "content" from undefined. I have tried accessing the same using XML call out which worked fine. Is there something am I missing, or something is wrong with the code?

@Pradeep Cherukupalli, The above code is working fine. Couple of changes in your code.

1. Add the response Content-Type

// set the response header

response.headers['Content-Type']='application/json';

2. Update the below line with in the IF condition.

context.setVariable('response.content', r.getResponse().content);

Thanks for reply <a rel="user" href="https://community.apigee.com/users/10073/ferose.html" nodeid="10073">@Mahammad Feroz</a>. Here is my code after your edits:


try {
  response.content = '';
  response.headers['Content-Type'] = 'application/json';
  var productsData = httpClient.get(
        'http://xxx/json/WebApi/GetDetails');
  print("call made to internal service");
  context.setVariable('responseawait', productsData);
  var r = context.getVariable('responseawait');
  if (r) {
    // retrieve the pending request from the context variable
    print("region entered");
    r.waitForComplete();
    print("processing done");
    if (r.isSuccess()) {
      print("entered success");
      print(r.getResponse().content);
      context.setVariable('response', r.getResponse().content);
      print(r.getResponse().content);
    }
    else 
    {
      print(r);
      throw 'Unable to process request';
    }
    //print(r.getResponse().content);
  }
} catch (err) {
  response.content.asJSON.error = err;
}

After this edit I am facing this error:

 {"error": "TypeError: Cannot find default value for object."} 

Can you please help me on this?

@Pradeep Cherukupalli, Update the below in your code.

context.setVariable('response.content', r.getResponse().content);

@Mahammad Feroz: I did that too the problem is code is not entering into r.IsSuccess() i tried tracing that so that block of code is not getting executed. Any ideas on the error?

Check to see that the backend is responding with a 200 code.

r.isSuccess() will return true if the http status code is 200 or 2xx. If the backend is saying "bad request" then your if statement will not evaluate to true.

Thanks @Dino, remote call is responding in <1s and I have tried setting timeLimit attribute to higher value like 10000. Also the backend is responding with 200 status code. I have only one callout in my proxy and only one policy as of now. So just wondering if async has got anything to do with this. Also can I make this call synchronous instead?

No, there is no "synchronous" option when using the httpClient from a JavaScript callout. You must call .waitForComplete(). If you want synchronous, then you can use as alternatives to JavaScript callouts:

  • ServiceCallout
  • ScriptTarget

At this point I am not clear on what problem you are having.

If r.isSuccess() is returning false, then the FALSE clause executes, right?

If r.usSuccess() returns true then the TRUE clause executes, right?

So... what exactly is the problem now?

  • What are you trying (show code AND configuration)
  • What are you expecting to see?
  • what are you actually seeing?

There is a tangled stream of comments and followups here. I am not surewhere we are at this point. You are having a problem, but I don't know what it is.

javascript-callout-0905.zipSorry for too many comments @Dino. This is what I am trying to achieve. So the expectation is calling a backend service A through JS callout and sending response back. I am attaching the code for what I have worked till now.

Hi Pradeep,

I'm just looking at your proxy now. Thanks for sending it.

It seems you are using a JS callout to make the backend call. I wonder why you would want to do that? The Apigee Proxy is designed to do that for you, via the target. If you are transforming a GET to a GET, you don't need a ServiceCallout or a JS Callout. You can just use a Target.

The ServiceCallout is intended to allow you to send an additional call, to a different system. The JS Callout is a general purpose policy, and one of the things you can do with it is use the httpClient to make an additional call. But these are additional, with respect to the target.

It seems to me you have just one system you want the proxy to connect to, and therefore that system should use the target. By doing so, essentially all of the logic in the JS module that you have written.... becomes extraneous. Unnecessary. The Target handles all of that for you. Just configure the URL and ... done.

Have a look at this API Proxy.

apiproxy-pradeep-20170908-20170908-093848.zip

@Dino thanks so much for this solution. Its quite simple in terms of what I needed to achieve. Thanks so much again for the sample code which made it lot easier to understand. I have question, so is it possible to have a single target end point and make conditional based requests. Also can I have JS in pre flow to modify my requests and set that as target end point URL.

@Mahammad Feroz

can you help me out in configuring the policy and call out for API call? waitForComplete with interval or without interval seems to be not working for me.

Is it possible the API call being made from the JavaScript callout is taking longer than 200ms? By default, JavaScript callouts are time-limited to 200ms.

If you perform a remote call from within the JS callout, it will sometimes take longer. In that case the Apigee runtime will stop the JS call, and execution will terminate.

You can extend the time Apigee gives to a JS callout via the policy configuration, the timeLimit attribute. Like this:

<Javascript name='JS-MyJavaScriptCallout' timeLimit='5000' >
  <Properties> ...
  </Properties>
  <ResourceURL>jsc://myJavascriptCallout.js</ResourceURL>
</Javascript>

But, this may still fail in some cases. It's possible for the remote system to take more than 5 seconds. You can handle such errors in your api proxy flow. Or, if you have the time limit in .waitForComplete(), then you can handle the timeout in the JS code itself, provided the time you pass to waitForComplete is less than the time you configure for timeLimit. (Do you understand why? )

If you would like to perform a simple GET, you can use the ServiceCallout instead, for better results.

See the documentation for more information.

@Pradeep Cherukupalli, I have used your code in my proxy and it's working fine. Looks like there is an issue with your backend service. Pls check both the response time & status code by calling backend service directly.

Thanks @Mahammad Feroz for taking time and helping me. It is some access problem with my backend service. Will get that fixed and will try out. Thanks again !!!