Using Node.JS app with specific Target URL Path

I am hosting a simple node.js application within an APIProxy which will accept traffic via:

Proxy Endpoint:

/tms-sales-order/details/hello122

Hosted Target Endpoint:

/sales-order-gl/svc/v1/sales-Order/hello122

Using Trace I see the Proxy Endpoint accepts the incoming request and performs its configured Flows. The issue happens when attempting to send to the HostedTarget backend located in "/resources/hosted" directory. Attaching images of the CURL command being sent via Trace to the HostedTarget. It is also good to note, this node.js app works perfectly local and works within the proxy itself when I simply set the index.js file to accept anything, i.e. "/**" rather than attempting to set the specific URL. As you can see from the CURL request going to the HostedTarget backend only shows https://localhost with no other path context or URI pathing. Also, the Runtime Logs show the Node.js is deployed and listening and the Build Logs show the API Proxy is also deployed successfully.

Below is configuration text for the following:

CURL Request Sent to HostedTarget Node.js applicaiton:

curl -X GET -H 'Accept: */*' -H 'Accept-Encoding: gzip,deflate' -H 'Authorization: *****' -H 'Cache-Control: no-cache' -H 'Content-Type: application/json' -H 'Postman-Token: e3246dd5-bfdc-4828-bf19-a06e0640c002' -H 'X-Forwarded-For: ' -H 'X-Forwarded-Port: 443' -H 'X-Forwarded-Proto: https' 'http://localhost'

Target/default.xml

HTTPTargetConnection:

<HTTPTargetConnection> 
  <Properties/> 
  <SSLInfo> 
    <Enabled>true</Enabled> 
  </SSLInfo> 
  <LoadBalancer> 
    <Server name="mockTarget"/> 
  </LoadBalancer> 
  <Path>Some Custom Path</Path> 
</HTTPTargetConnection> 
<HostedTarget/>

resources/hosted index.js:

// Simple HelloWorld. 
app.get('SOME CUSTOM PATH', function (req, res) {
 console.log(req.query);
 res.send("Hello World!");
});

Error in the trace log says a couple of different things in the trace flow:

error.class com.apigee.errors.http.server.BadGateway
error.content { "status" : "500", "message": "Unhandled Error", "info" : "Provide URL " }
flow.edge.error.reason Internal Server Error
Body
{ 
  "status" : "500",
  "message": "Unhandled Error",
  "info" : "Provide URL "
}
		

Any thoughts on how I can get the <HostedTarget/> to send a context path and URI to the hosted node.js application instead of just https://locahhost? If I have the node.js app accept anything via "/**" it works, but this is not what I am trying to do.....All help is appreciated.

0 3 3,214
3 REPLIES 3

The error you're getting appears to be an Express error, and is not an Edge or Hosted Target error. To see what the request path is, I'd suggest throwing in a non-Express request handler in that logs the request path/URL/... to see what is going on. I don't think the HostedTarget does any path manipulation from the Edge proxy to the Hosted Target so I'd be interested in seeing these details. As for why your Express application is getting "http://localhost" for the context path, isn't that what you're issuing the cURL for? To be honest, I'm having a hard time following so please be patient.

It looks to me, to be some confusion as to whether you are using a hosted target or an HTTP Target endpoint.

A target endpoint definition that uses a Hosted target should be like this:

<TargetEndpoint name="hosted">
    <Description/>
    <FaultRules/>
    <PreFlow name="PreFlow">
        <Request/>
        <Response/>
    </PreFlow>
    <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
    <Flows/>
    <HostedTarget/> <!-- note -->
</TargetEndpoint>

a target that uses an HTTP Endpoint looks like this:

<TargetEndpoint name="target-1">
 ...
  <Flows/>
  <HTTPTargetConnection>
    <SSLInfo>
      <Enabled>true</Enabled>
      <IgnoreValidationErrors>false</IgnoreValidationErrors>
    </SSLInfo>
    <Properties/>
    <URL>https://buoyant-climate-208500.appspot.com</URL>
  </HTTPTargetConnection>
</TargetEndpoint>

Your target endpoint should include exactly one of HttpTargetConnection and HostedTarget. Not both. AFAIK, there is unfortunately nothing in the Apigee Edge admin API that rejects a target endpoint that includes both HTTPTargetConnection as well as HostedTarget. Despite that, there is also no clear definition of the behavior of a target endpoint that includes both of those elements. In short, don't include both elements.


Also: If you correctly configure the target to be "HostedTarget", then the backend nodejs server receives a path that does not include the basepath of the proxy endpoint.

Example

Suppose you have an API Proxy like this

proxyendpoint

basepath /foo

target endpoint

type: hosted

Code like this in your nodejs server:

const svr = http.createServer(function(req, resp) {
  resp.setHeader("Content-Type", "application/json");
  resp.end(JSON.stringify({
    date: new Date(),
    inboundUrl : req.url,
    msg: 'Hello, World!'
  }));
});

If you send in a request like

curl -X GET $apiendpoint/foo/bar

...then your nodejs code will receive a req.url of "/bar" .

Sorry took so long to get back to you, I wanted to say thanks for your response. I took a little extra time responding because I kept being "on the verge" of figuring this issue out. Just so happens I was, and the solution is below. Perhaps looking at the solution you will see what I was attempting to do:

Solution:

- Create Java Script Policy (JS-targetrewright)

- <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Javascript async="false" continueOnError="false" enabled="true" timeLimit="200" name="JS-targetrewrite"> <DisplayName>JS-targetrewrite</DisplayName> <Properties/> <ResourceURL>jsc://JS-targetRewrite.js</ResourceURL> </Javascript>

- Create Java Script (JS-targetrewrite)

- if (context.flow=="TARGET_REQ_FLOW") { context.setVariable("request.verb", "GET"); var url = "http://localhost/<someURLHere>" context.setVariable("target.url", url + "<SomeURIHere>"); }

- Call the JS-targetrewright in the "Target Endpoint Proxy Pre-Flow"

- <Step> <Name>JS-targetrewrite</Name> </Step>

Once I added this to the proxy, I was then able to send a request to my API Proxy and it then sent the request to the specified Target URL being hosted via <HostedTarget/>. You can see the CURL request sent to <HostedTarget/> is now updated with the full base path and URI rather than just saying "http://localhost":

curl -X GET -H 'Accept: */*' -H 'Accept-Encoding: gzip,deflate' -H 'Authorization: *****' -H 'Cache-Control: no-cache' -H 'Content-Type: application/json' -H 'Postman-Token: 741d2d1f-2b61-495f-8c1c-eae4b3627f08' -H 'X-Forwarded-For: 198.90.15.66' -H 'X-Forwarded-Port: 443' -H 'X-Forwarded-Proto: https' 'http://localhost/<updated>/<basepath>/<uri>'

Again, thanks for taking a look at this. I wanted to post the complete solution for others who are attempting to do the same thing. There are a TON of threads where folks have attempted doing this but could not find a solution that worked.