Send Docs Feedback

JavaScript object model

This topic discusses the Apigee Edge JavaScript Object Model. It's important you understand this model if you intend to use the JavaScript policy to add custom JavaScript to an API proxy.

About the Edge JavaScript object model

The Apigee Edge JavaScript object model defines objects with associated properties that are available to JavaScript code executing within an Apigee Edge proxy flow. You use the JavaScript policy to attach this custom code to an API proxy flow.

The objects defined by this model have scope within the API proxy flow, which means that certain objects and properties are available only at specific points in the flow. When your JavaScript is executed, a scope is created for the execution. In that scope, these three object references are created: context, request, and response.

The context object

The context object has global scope. It is available everywhere within the API proxy flow. It has four child objects: proxyRequest, proxyResponse, targetRequest, targetResponse. These child objects are scoped to the ambient request and response, either the proxy request and response or the target request and response. For example, if the JavaScript policy executes in the proxy endpoint part of the flow, then the context.proxyRequest and context.proxyResponse objects are in scope. If the JavaScript runs in a target flow, then the context.targetRequest and context.targetResponse objects are in scope.

The context object also has properties and methods, which are described in detail in this topic. For example, the following JavaScript code example uses the context.flow property and calls the get/setVariable() methods on context

if (context.flow=="PROXY_REQ_FLOW") {
     var woeid = context.getVariable("request.formparam.woeid");
     context.setVariable("location.woeid", woeid);
}

These methods interact directly with flow variables. The context.flow property value is reflects the current flow scope. In the proxy request flow, it's set to the constant PROXY_REQ_FLOW. If in the target respone flow, it's set to TARGET_RESP_FLOW. This constant is handy for executing scope-specific code. The getter lets you get flow variables and the setter lets you set flow variables. These variables are generally available in the proxy flow and can be consumed by other policies.

See "context object reference" below for more details and examples.

The request and response objects

The request and response objects are "shorthand" references to the ambient request and response, either the proxy request and response or the target request and response. The objects these variables refer to depend upon the context in which the JavaScript policy executes. If the JavaScript runs in the flow of a proxy endpoint, then the request and response variables refer to context.proxyRequest and context.ProxyResponse. If the JavaScript runs in a target flow, then the variables refer to the context.targetRequest and context.targetResponse.

The print() function

The Javascript object model includes a print() function that you can use to output debug information to the Edge Trace tool. See "Debug with JavaScript print() statements". 

context object reference

A context object is created for each request/response transaction executed by an API proxy. The context object exposes methods to get, set, and remove variables related to each transaction.

Variables define properties specific to a transaction. The time of day, the locale of the requesting client, the user-agent of the requesting client, and the URL of the target service are all examples of variables that are available in the context. Therefore, context is useful for building logic that relies on these properties to execute custom behavior.

See Variables reference and Extract Variables policy.

context object summary

This table briefly describes the context object and its children, and lists the properties that are bound to each.

Name Description Properties
context A wrapper for the message processing pipeline context and the request and response Flows that are executed by the ProxyEndpoint and TargetEndpoint. flow, session
context.proxyRequest An object that represents the inbound request message to the ProxyEndpoint (from the requesting app to the API proxy) headers, query parameters, method, body, url
context.targetRequest An object that represents the outbound request message from the TargetEndpoint (from the API proxy to the backend service). headers, query parameters, method, body, url
context.targetResponse An object that represents the inbound target response message (from the backend service to the API proxy) headers, content, status
context.proxyResponse An object that represents the outbound proxy response message (from the API proxy to the requesting app) headers, content, status
context.flow The name of the current flow. See context.flow below.
context.session A map of name/value pairs that you can use to pass objects between two different steps executing in the same context. For example: context.session['key'] = 123. See context.session below.

context object methods

context.getVariable()

Retrieves the value of a pre-defined or custom variable.

Syntax

context.getVariable("variable-name");

Example

To get the value for the current year:

var year = context.getVariable('system.time.year');

context.setVariable()

Sets the value for a custom variable or for any writable pre-defined variables.

Syntax

context.setVariable("variable-name", value);

Example

A common scenario for setting a variable is when an API proxy must dynamically write the target URL. The following JavaScript obtains the value of a variable called WOEID.location, appends that value as a query parameter to the URL http://weather.yahooapis.com/forecastrss?w=, and then sets the pre-defined target.url to that value.

context.setVariable("target.url", "http://weather.yahooapis.com/forecastrss?w="+context.getVariable("WOEID.location"));

Any writable pre-defined variable and any custom variable can be dynamically set from JavaScript. For a complete list of pre-defined variables, see Variables reference.

context.removeVariable()

Removes a variable from the context.

Syntax

context.removeVariable('variable-name');

context object properties

context.flow

The flow property is a string that identifies the current API proxy flow. This property is used to indicate the Flow to which the JavaScript is attached. Supported values are:

  • PROXY_REQ_FLOW
  • PROXY_RESP_FLOW
  • TARGET_REQ_FLOW
  • TARGET_RESP_FLOW

Each Flow name encompasses the PreFlow, PostFlow, and any conditional Flows defined in the ProxyEndpoint(s) or TargetEndpoint(s).

This optional property is useful when common JavaScript is executed in more than one Flow, but might vary its behavior depending on the Flow in which it executes. Use the Flow property for JavaScript modules intended to be reused in multiple API proxies, in which the code is required to check the current Flow before executing logic.

Example

Set an HTTP header only on the targetRequest Flow:

if (context.flow=="TARGET_REQ_FLOW") {
     context.targetRequest.headers['TARGET-HEADER-X']='foo';
}

Set the content only on the proxyResponse Flow:

if (context.flow=="PROXY_RESP_FLOW") {
     context.proxyResponse.content='bar';
}

context.session

A map of name/value pairs that can be used to pass objects between two policies executing within the same message context.

Example

Set a value in the session:

context.session['key']  = 123;

Get the value from the session:

var value = context.session['key']; // 123

For a working example that uses the context.session object see the Asynchronous callout sample API proxy.

context object children

As shown below, a complete API proxy Flow encompasses four distinct phases, each of which has an associated message object that is a child of the context object:

  • context.proxyRequest: The inbound request message received from the requesting client.
  • context.targetRequest: The outbound request message sent to the backend service.
  • context.proxyResponse: The outbound response message returned to the requesting client.
  • context.targetResponse: The inbound request message received from the backend service.

The following sections describe the methods and properites of these objects:

context.*Request child objects

For each HTTP transaction the executes in an API proxy, two request message objects are created: one "inbound" (the request from the client) and one "outbound" (the request generated by the API proxy and submitted to the backend target.)

The context object has child objects that represent these request messages: context.proxyRequest and context.targetRequest. These objects let you access properties within the request flow that is in scope when your JavaScript code executes.

Note: You can also use the "shorthand" object request to access these properties in a request flow. The request object refers to either context.proxyRequest or context.targetRequest, depending on where in the flow your JavaScript code executes.

context.*Request child object properties

Property name Description
url

The url property is a read/write convenience property that combines scheme, host, port, path and query parameters for the targetRequest.

The complete URL of the request is composed of the following properties:

  • protocol: The protocol of the URL (for example, HTTP, HTTPS)
  • port: The port (for example, :80, :443)
  • host: The host of the URL (for example, www.example.com)
  • path: The path of the URI (for example, /v1/weather)

When getting url, a URL is returned in the following format:

protocol://host:port/path?queryParams

Examples:

context.targetRequest.url = 'http://www.example.com/path?q1=1'
context.targetRequest.protocol ='https';
headers

HTTP request headers as a mapping of String => List

Examples:

For this HTTP request:

POST /v1/blogs HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer ylSkZIjbdWybfs4fUQe9BqP0LH5Z
The following JavaScript:
context.proxyRequest.headers['Content-Type'];
context.proxyRequest.headers['Authorization'];

will return the following values

application/json
Bearer ylSkZIjbdWybfs4fUQe9BqP0LH5Z
queryParams

The request message query parameters as a mapping of String => List.

Examples:

"?city=PaloAlto&city=NewYork"

can be accessed as:

context.proxyRequest.queryParams['city'];  // == 'PaloAlto'
context.proxyRequest.queryParams['city'][0]     // == 'PaloAlto'
context.proxyRequest.queryParams['city'][1];    // == 'NewYork' 
context.proxyRequest.queryParams['city'].length(); // == 2  
formParams

The request message query parameters as a mapping of String => List.

Examples:

"PaloAlto&city=NewYork"

can be accessed as:

context.proxyRequest.formParams['city'];  // == 'PaloAlto'
context.proxyRequest.formParams['city'][0]     // == 'PaloAlto'
context.proxyRequest.formParams['city'][1];    // == 'NewYork' 
context.proxyRequest.formParams['city'].length(); // == 2  
method

The HTTP verb (GET, POST, PUT, DELETE. PATCH, etc.) associated with the request

Examples:

For this request:

POST /v1/blogs HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer ylSkZIjbdWybfs4fUQe9BqP0LH5Z

The following JavaScript:

context.proxyRequest.method;

will return the following value

POST
body

The message body (payload) of the HTTP request.

The request body has the following members:

  • context.targetRequest.body.asXML;
  • context.targetRequest.body.asJSON;
  • context.targetRequest.body.asForm;

Examples:

For an XML body:

<customer number='1'>
<name>Fred<name/>
<customer/>

To access the elements of the XML object as follows:

var name = context.targetRequest.body.asXML.name;

To access XML attributes attributes, use the @ notation.

var number = context.targetRequest.body.asXML.@number;

For a JSON request body:

{ 
"a":  1 , 
"b" : "2" 
}
var a = context.proxyRequest.body.asJSON.a;    // == 1
var b = context.proxyRequest.body.asJSON.b;    // == 2

To read form parameters:

"vehicle=Car&vehicle=Truck"
v0 = context.proxyRequest.body.asForm['vehicle'][0]; 
v1 = context.proxyRequest.body.asForm['vehicle'][1];

context.*Response child objects

For each HTTP transaction the executes in an API proxy, two response message objects are created: one "inbound" (the response from the backend service) and one "outbound" (the response sent back to the client.)

The context object has child objects that represent these response messages: context.proxyResponse and context.targetResponse. These objects let you access properties within the response flow that is in scope when your JavaScript code executes.

Note: You can also use the "shorthand" object response to access these properties from a response flow. The response object refers to either context.proxyResponse or context.targetResponse, depending on where in the flow your JavaScript code executes.

context.*Response object properties

Property name Description
headers

The HTTP headers of the response message as a mapping of String => List.

Example:

var cookie = context.targetResponse.headers['Set-Cookie'];
status

The status code with status message as a property. Both status code and status message are available as properties.

Example:

var status = context.targetResponse.status;        // 200
var msg = context.targetResponse.status.message;   // "OK"
content

The HTTP body (payload content) of the response message.

Response content has the following members:

context.targetResponse.content.asXML;  
context.targetResponse.content.asJSON;
 

Using .asXML notation

There is a handy way to walk through an XML document using the .asXML notation. This section describes how to use this notation, and how it differs from request.content and context.proxyRequest.content.

For example:

request.content.asXML

or

context.proxyRequest.content.asXML

Both the *.content and *.content.asXML forms can be used in a string context, and JavaScript will coerce them to become strings. In the former case (*.content), the string includes all declarations as well as XML comments. In the latter case (*.content.asXML), the string value of the result is cleaned of declarations and comments.

Example

msg.content:

<?xml version="1.0" encoding="UTF-8"?>
<yahoo:error xmlns:yahoo="http://yahooapis.com/v1/base.rng" xml:lang="en-US">
   <yahoo:description>Please provide valid credentials. OAuth oauth_problem="unable_to_determine_oauth_type", realm="yahooapis.com"
   </yahoo:description>
</yahoo:error>
<!-- mg023.mail.gq1.yahoo.com uncompressed/chunked Sat Dec 14 01:23:35 UTC 2013 -->

msg.content.asXML:

<?xml version="1.0" encoding="UTF-8"?>
<yahoo:error xmlns:yahoo="http://yahooapis.com/v1/base.rng" xml:lang="en-US">
   <yahoo:description>Please provide valid credentials. OAuth oauth_problem="unable_to_determine_oauth_type", realm="yahooapis.com"
   </yahoo:description>
</yahoo:error>

Furthermore, you can use the .asXML form to traverse the XML hierarchy, by specifying the names of elements and attributes. It is not possible to traverse the hierarchy using the other syntax.

Debug with JavaScript print() statements

If you're using the JavaScript policy to execute custom JavaScript code, note that you can use the print() function to output debug information to the Trace tool. This function is available directly through the JavaScript object model. For example:

if (context.flow=="PROXY_REQ_FLOW") {
     print("In proxy request flow");
     var woeid = context.getVariable("request.queryparam.woeid");
     print("Got query param: " + woeid);
     context.setVariable("location.woeid", woeid);
     print("Set query param: " + context.getVariable("location.woeid"));
}


if (context.flow=="TARGET_REQ_FLOW") {
     print("In target request flow");
     var woeid = context.getVariable("location.woeid");
     var url = "http://weather.yahooapis.com/forecastrss?"
     context.setVariable("target.url", url + "w=" + woeid);
     print("callout to URL: ", context.getVariable("target.url"));
}

To see the output, select Output from all transactions at the bottom of the Trace window:

You can also find out put in the Trace property called "stepExecution-stdout". 

Making JavaScript callouts with httpClient

Use httpClient to make multiple, parallel, asynchronous HTTP requests to any URL from within custom JavaScript code executing in an API proxy flow. The httpClient object is exposed by the Apigee Edge Javascript object model

About httpClient

The httpClient object is exposed to custom JavaScript code running on Apigee Edge through the JavaScript object model. To attach custom JavaScript to an API proxy, you use the JavaScript policy. When the policy runs, the custom JavaScript code executes.

The httpClient object is useful for developing composite services or mashups. For example, you can consolidate multiple backend calls into a single API method. This object is commonly used as an alternative to the ServiceCallout policy.

Here's a basic usage pattern. Instantiate a Request object, assign to it a URL (e.g., to a backend service you wish to call), and call httpClient.send with that request object.

var myRequest = new Request();
myRequest.url = "http://www.example.com";
var exchangeObj = httpClient.send(myRequest);

httpClient Reference

The HTTP Client exposes two methods: get() and send().

httpClient.get()

A convenience method for simple HTTP GETs, with no support for HTTP headers.

Usage

var exchangeObj = httpClient.get(url);

Returns

The method returns an exchange object. This object has no properties, and it exposes the following methods:

  • isError(): (boolean) Returns true if the httpClient was unable to connect to the server. HTTP status codes 4xx and 5xx result in isError() false, as the connection completed and a valid response code was returned. If isError() returns true, then a call to getResponse() returns the JavaScript undefined.
  • isSuccess(): (boolean) Returns true if the send was complete and successful.
  • isComplete(): (boolean) Returns true if the request is complete.
  • waitForComplete(): Pauses the thread until the request is complete (by success or error).
  • getResponse(): (object) Returns the response object if the httpClient.send() was complete and successful. The returned object has the identical methods and properties as the content.proxyResponse object documented in the Apigee Edge Javascript object model
  • getError(): (string) If the call to httpClient.send() resulted in an error, returns the error message as a string.

You can use the exchange object later to get the actual HTTP response, or to check whether the response has timed out. For example:

var ex1 = httpClient.get("http://www.example.com?api1"); 
context.session["ex1"] = ex1;  // Put the object into the session 

var ex2 = httpClient.get("http://www.example.com?api2");
context.session["ex2"] = ex2;  // Put the object into the session 

You can access the returned object later during flow processing. Here, we access the exchange objects in another JavaScript policy by first getting the exchange objects from the session.

ex1.waitForComplete();      // Thread is paused until the response is returned or error or step time limit has been reached.
ex2.waitForComplete(100);   // Thread is paused for a maximum of 100 ms.

var ex1 = context.session["ex1"]; // Get exchange objs from the session.
var ex2 = context.session["ex2"];

if (ex1.isSuccess() && ex2.isSuccess()) {
   response.content = ex1.getResponse().content + ex2.getResponse().content;
}

Example:

var exchangeObj = httpClient.get("http://www.example.com");

httpClient.send()

Lets you send a fully configured Request object containing the properties of the HTTP request.

Note: The Request object has the identical properties as the response object documented in the JavaScript object model.

Usage

var request = new Request(url, operation, headers);
var exchangeObj = httpClient.get(request);

Returns

A call to httpClient.send() returns an exchange object. For details about this object, see the httpclient.get() method description.

Example 1:

var headers = {'X-SOME-HEADER' : 'some value' };
var myRequest = new Request("http://www.example.com","GET",headers);
var exchange = httpClient.send(myRequest); 

Example 2:

var headers = {'Content-Type : 'application/xml' };
var myRequest = new Request("http://www.example.com","POST",headers,"");
var exchange = httpClient.send(myRequest);

Example 3:

Call a backend service, retrieve the response payload, and extract a variable from it.

(Note: You can find the complete, working code from which this snippet was extracted in the "Outbound OAuth" sample on GitHub.) 

/**
 * Retrieve an access token for the Microsoft Translator API
 * http://msdn.microsoft.com/en-us/library/hh454950.aspx
 */
function getAccessToken() {
  var bodyObj = {
    'grant_type': translatorApi.grantType,
    'scope': translatorApi.scopeUrl,
    'client_id': translatorApi.clientId,
    'client_secret': translatorApi.clientSecret
  };

  var req = new Request(translatorApi.authUrl, 'POST', {}, serializeQuery(bodyObj));
  var exchange = httpClient.send(req);

  // Wait for the asynchronous POST request to finish
  exchange.waitForComplete();

  if (exchange.isSuccess()) {
    var responseObj = exchange.getResponse().content.asJSON;

    if (responseObj.error) {
      throw new Error(responseObj.error_description);
    }

    return responseObj.access_token;
  } else if (exchange.isError()) {
    throw new Error(exchange.getError());
  }
}

context.setVariable('twitter-translate.apiAccessToken', getAccessToken());

Example 4:

Track the status of an HTTP request.

function userCheck() {
  var url = getAppServicesUrl() + '/users/' + username,
      headers = {
        Authorization : 'Bearer ' + appServicesAccessToken
      },
      req = new Request(url, 'GET', headers),
      exchange = httpClient.send(req),
      response, status;

  // Wait for the asynchronous GET request to finish
  exchange.waitForComplete();

  // get the response object from the exchange
  response = exchange.getResponse();

  // get the HTTP status code from the response
  status = response.status;

  if (status == 200) {
    context.setVariable('userCheck.trace', 'user exists');
  }
  else if (status == 404) {
    context.setVariable('userCheck.trace', 'user does not exist');
  }
  else {
    context.setVariable('userCheck.trace', 'user-inquiry-status:' + status);
  }
  return true;
}

 

Using the JavaScript policy

Use the JavaScript policy to attach custom JavaScript code to a proxy flow. See JavaScript policy.

Related topics

 

Apigee Community articles

You can find these related articles on the Apigee Community:

Help or comments?