—Rate this article—
 

JavaScript object model

Background

As an API developer you are undoubtedly familiar with JavaScript. Apigee Edge enables you to leverage your background with JavaScript to implement custom API behavior without having to change the code of your APIs or backend services.

Apigee Edge can be used as a managed container for executing your custom JavaScript. You write the custom logic, and Apigee provides the container, security, management, monitoring, analytics.

The benefit of this approach is that it lets you offload common functions like rate limiting, API key provisioning, OAuth authorization, transformations, which frees you to focus on coding the API behavior that is the most innovative and interesting.

You can combine Apigee's out-of-the-box policies with your own custom JavaScript to implement custom behavior securely and reliably.

This document is intended to be an overview and a reference.

For working JavaScript code samples, see the Apigee Edge samples on GitHub.

Overview

If you have a good grasp of JavaScript, you just need to become familiar with Apigee's object model. JavaScript that executes in a browser relies on the "browser object model", or BOM. JavaScript that executes on Apigee Edge relies on a message-oriented object model. This object model defines request, response, and context objects with associated properties. This document describes these objects and their properties.

Apigee Edge relies on Rhino, which is an open-source implementation of JavaScript written in Java. Apigee also uses E4X, which is an extension of JavaScript that adds support for XML.

When your JavaScript is executed in an API proxy Flow, a scope is created for the execution.

A set of object references is created in the scope:

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

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 methods

getVariable(): Retrieves the value of a pre-defined or custom variable.

For example, to get the value for the current year:

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

setVariable(): Sets the value for a custom variable or for any writable pre-defined variables.

var org = context.setVariable('organization.name.myorg', value);

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 target.url.

WOEID.location is a custom variable. target.url.target.url is a pre-defined variable.

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.

removeVariable(): Removes a variable from the context.

var org = context.removeVariable('organization.name.myorg');

Context properties

flow: The name of the current Flow

The flow property is a string that identifies the current 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.

For 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';
}

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

For example, you can set the context as follows:

context.session['key']  = 123;

When needed you can get the context as follows::

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

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

Messages

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

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

Request messages

The context provides access to objects that represent request messages. 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.)

These two request objects are called proxyRequest and targetRequest, respectively. The proxyRequest and targetRequest objects enable your JavaScript to specify which message object within the context should be the target of a JavaScript operation.

Request 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
queryParameters

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  
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.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];

Response messages

Apigee Edge generates two response objects: one for the ProxyEndpoint and one for the TargetEndpoint.

Before the targetRequest PreFlow has executed, The response object has a value of undefined.

Response object properties

Property name Description
headers

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

Examples

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.

Examples

var status = context.targetResponse.content.status;        // 200
var msg = context.targetResponse.content.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;

Examples

Examples:

For an XML response:

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

You can access the elements of the XML object as follows:

var name = context.proxyResponse.content.asXML.name;

You can access the attributes of the XML object using the @ sign notation.

var number = context.proxyResponse.content.asXML.@number;

For JSON response:

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

httpClient

The JavaScript HTTP Client can be used to make multiple parallel, asynchronous HTTP requests to any URL. This makes it useful for developing composite services, as well as for consolidating multiple backend calls into a single API method. You can use the HTTP client as an alternative to the ServiceCallout policy.

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

get()

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

Example:

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

send()

Enables full configuration of the request message using the request object to contain the properties of the HTTP request.

The httpClient request object is identical in type to the request object generated by a standard Flow.

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

Alternatively:

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

or

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

The calls to get() and send() immediately return an object that can used later to get the actual HTTP response, or to check whether the response has timed out.

Example:

var ex1 = httpClient.get("http://www.example.com?api1"); 
var ex2 = httpClient.get("http://www.example.com?api2");

The returned object can be accessed later during flow processing, for example by another JavaScript policy:

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.

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

A call to httpClient.send() returns an exchange object. The exchange object object has no properties, and it exposes the following methods:

  • isError(): 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(): returns true if the send was complete and successful.
  • isComplete(): returns true if the request is complete.
  • waitForComplete(): pauses the thread until the requests is complete (by success or error).
  • getResponse(): returns the response object if the httpClient.send() was complete and successful. The httpClient response object is identical in type to the response object generated by a standard Flow.
  • getError(): If the call to httpClient.send() resulted in an error, returns the error message as a string.

For example, to 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');

For example, to use httpClient to make a call to retrieve an OAuth access token:

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());
  }
}

Policy attachment

JavaScript is attached to API proxy Flow configuration using the Javascript Policy type.

See JavaScript policy.

Learn more

This document is intended to be an overview and a reference.

For basic samples and instructions, see Programming API proxies with JavaScript .

For working JavaScript code samples, see the Apigee Edge samples on GitHub.

For an example of more advanced JavaScript programming on Apigee Edge, see Implementing HTTP clients in JavaScript.

Help or comments?

  • Something's not working: See Apigee Support
  • Something's wrong with the docs: Click Send Feedback in the lower right.
    (Incorrect? Unclear? Broken link? Typo?)