As an API developer you are undoubtedly familiar with JavaScript. The Apigee API Platform 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's API Platform 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, and such, freeing you to focus on innovation.

In short, you can combine Apigee's out-of-the-box policies with your own custom JavaScript to implement sophisticated and reliable behavior that meets the needs of your apps and, if you expose APIs, your API developers.

Learn more

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

For basic samples and instructions on getting JavaScript running on the API Platform refer to the Developer Guide topic, Use JavaScript to customize an API

For an example of advanced JavaScript programming on the API Platform, see Building a composite service using JavaScript

For working JavaScript code samples, see the API Platform samples on GitHub.

The Apigee JavaScript Object Model

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 the Apigee API Platform 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 provides samples to get you started writing your own JavaScript against the Apigee object model.

Apigee's API Platform 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 Apigee Flow, a scope is created for the execution.

A set of object references is created in the scope:

Name Description Properties
proxyRequest An object that represents the inbound proxy request message (from the requesting app to the API Platform) headers, query parameters, method, body, url
targetRequest An object that represents the outbound target request message (from the API Platform to the backend service). headers, query parameters, method, body, url
targetResponse An object that represents the inbound target response message (from the backend service to the API Platform) headers, content, status
proxyResponse An object that represents the outbound proxy response message (from the API Platform to the requesting app) headers, content, status
context A wrapper for the message processing pipeline context and the request and response flows that are executed by the proxy and target endpoints. flow, session

Proxy and Target Request and Response

The proxyRequest and targetRequest objects enable your JavaScript to specify which message object within the context should be the target of a JavaScript operation.

As shown below, the request Flow encompasses:

  • 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.

proxyRequest and targetRequest

The API Platform generates two request objects: one for the ProxyEndpoint and one for the TargetEndpoint.

Request Object Properties

headers: HTTP request headers as a mapping of String => List

Example:

proxyRequest.headers['via'];  
proxyRequest.headers['via'][1];

queryParameters: The query params as a mapping of String => List

Example:

This query string:

"?city=PaloAlto&city=NewYork"

can be accessed as:

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

method: The HTTP verb associated with the request

"GET" |  "POST" | "PUT" | "DELETE" etc.

body: The message body (payload) of the HTTP request

The request body has the following members:

targetRequest.body.asXML;   
targetRequest.body.asJSON; 
targetRequest.body.asForm;

Examples:

For an XML body:

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

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

var name = targetRequest.asXML.name;

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

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

For a JSON request body:

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

For form parameters:

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

url:The complete URL of the request

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

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

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

protocol://host:port/path?queryParams

Example:

targetRequest.url = ‘http://www.example.com/path?q1=1
targetRequest.protocol =’https’;

results in

(targetRequest.url == ‘https://www.example.com/path?q1=1)

proxyResponse and targetResponse

The API Platform generates two response objects: one for the ProxyEndpoint and one for the TargetEndpoint.

(The response object has a value of undefined until the targetRequest PreFlow executes.)

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

Example:

var date = targetResponse.headers['Date'];

content: The body of the response message.

Response content has the following members:

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

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 = proxyResponse.content.asXML.name;

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

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

For JSON response:

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

status: The status code with status message as a property

Example:

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

Context

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

Variables can be thought of as 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.

You can use the predefined variables exposed by the API Platform, or you can define you own custom variables (which typically map to message content specific to your applications.)

Note: Some variables are set by default (for example, all HTTP request headers are always populated in the format request.header.{header_name}.) Other variables must be populated by configuring an ExtractVariables policy. See Extract message content using ExtractVariables for reference, and see Developer Guide topic Analyze API message content using custom analytics for a use case on defining and populating custom variables based on message content.

See the Variables reference for a list of variables that can be accessed by calling context in your JavaScript.

getVariable(): Retrieves the value of a variable

For example, to populate a variable with the value for the current year:

var year = context.getVariable(‘system.time.year’);

setVariable(): Sets a variable

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

removeVariable(): Removes a variable.

var org = context.removeVariable(‘organization.name.myorg’, value);

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. Possible 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 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. For example, you can use the Flow property when you have implemented a JavaScript module that you reuse in different API proxies, and therefore need 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") {
     request.headers['TARGET-HEADER-X]='foo';
}

Set the content only on the proxyResponse Flow:

if (context.flow=="PROXY_RESP_FLOW") {
     response.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

Example:

context.session[‘key’]  = 123;
…
var value = context.session[‘key’];  // 123

Headers, Query Parameters, and Form Variables

Headers, Query Parameters and Form variables all share a common format: a name/value pair that maps one string to another string.

The format:

proxyResponse.headers[‘foo’] = ‘bar’;

The name in a name/value pair is always a string, but the value may be either a string or a list of strings.

If the value is a list, then the members of the list can be accessed by the array operator

targetResponse.headers[‘foo’][0] = ‘bar’;

in practice targetResponse.headers[‘foo’] is response.headers[‘foo’][0];

For example if the following HTTP headers are set in an HTTP response:

Content-Type: application/json
Via: 1.0 fred
Via: 1.1 example.com
targetResponse.headers[‘Content-Type’]      // “application/json”
targetResponse.headers[‘Content-Type’][0]   // “application/json”
targetResponse.headers[‘Content-Type’][1]   // undefined
targetResponse.headers[‘Via’]        // “1.0 fred”
targetResponse.headers[‘Via’][0]     // “1.0 fred”
targetResponse.headers[‘Via’][1]     // “1.1 example.com”

HTTP Client

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 API calls into a single method. You can use the HTTP client as an alternative to the Service Callout policy.

For a detailed walkthrough of a composite service built with the HTTP Client, see Building a composite service using JavaScript

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, and described above..

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.

The object returned by a call to httpClient exposes the following methods:

  • isError()
  • isSuccess()
  • isComplete()
  • waitForComplete()
  • getResponse()
  • getError()

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
}

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

For the complete code see the Twitter Translate JavaScript samples on Github.

Learn more

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

For basic samples and instructions, refer to the Developer Guide topic, Use JavaScript to customize an API

For working JavaScript code samples, see the API Platform samples on GitHub.

For an example of more advanced JavaScript programming on the API Platform, see Building a composite service using JavaScript

For assistance, post to the Developer Forum.