The topic Use JavaScript to customize an API demonstrates how to write and execute simple JavaScript in an API flow. This topic builds on those examples to demonstrate how to create a standalone composite service by attaching an entire JavaScript app to an API facade, using the API Platform as a managed container for your code.
The JavaScript sample app in this topic calls out from the API Platform to two third-party APIs, parses and combines the results in a JSON response to the requesting client. Unlike other API facades, the API facade that you create here does not define a TargetEndpoint. Instead, the JavaScript app that executes in the ProxyEndpoint functions as an HTTP client, making two sequential API calls before returning a JSON response to the client app. No TargetEndpoint configuration is required.
The complete code and API proxy for this sample are available in the API Platform samples on GitHub.The composite service consumes two public APIs:
- The Google Geocoding API
- The Google Elevation API
The composite service returns a JSON response that includes the geocoded location for the center of the postal code, combined with the elevation at that location.
The composite service also exposes its own API, which takes two query parameters:
- postalcode: A postal code valid in the country specified
- country: A two-letter country code
Thus, the API that you expose can be documented and published using the Apigee API Console by using the sample WADL and following instructions provided at the end of this topic.
The steps involved in attaching and deploying the JavaScript are the same as those covered in Use JavaScript to customize an API.
Creating a JavaScript resource
The following JavaScript is written to the Apigee JavaScript object model and can be stored in a file called MashItUp.js under apiproxy/resources/jsc.
Initialize any responses
response.content = '';
response.headers['Content-Type'] = 'application/json';
try {
if ((request.queryParams.postalcode == undefined) ||
(request.queryParams.country == undefined)) {
throw '"postalcode" and "country" query parameters are required';
}
Construct a URL and call the geocoding API
var geocoding = httpClient.get(
'http://maps.googleapis.com/maps/api/geocode/json?address=' +
request.queryParams.postalcode +
'®ion=' + request.queryParams.country +
'&sensor=false');
geocoding.waitForComplete();
if (!geocoding.isSuccess()) {
throw 'Error contacting geocoding web service';
}
Parse the JSON response
The JSON response from the geocoding API is parsed into a JavaScript object
geocodeResponse = geocoding.getResponse().content.asJSON;
if (geocodeResponse.status != 'OK') {
throw 'Error returned from geocoding web service: ' + geocodeResponse.status;
}
Retrieve latitude and longitude from the response
Populate two variables with the latitude and longitude returned by the geocoding service.
var lat = geocodeResponse.results[0].geometry.location.lat;
var lng = geocodeResponse.results[0].geometry.location.lng;
Construct a URL and call the elevation API
Using the latitude and longitude returned from the geocoding service, construct a request to the elevation API.
var altitude = httpClient.get(
'http://maps.googleapis.com/maps/api/elevation/json?sensor=false&locations=' +
lat + ',' + lng);
altitude.waitForComplete();
if (!altitude.isSuccess()) {
throw 'Error contacting altitude web service';
}
altitudeResponse = altitude.getResponse().content.asJSON;
if (altitudeResponse.status != 'OK') {
throw 'Error returned from altitude web service: ' + altitudeResponse.status;
}
Retrieve elevation from the response
Using JavaScript, populate a variable with the elevation returned by the elevation service.
var alt = altitudeResponse.results[0].elevation;
Assemble the parts of the response as a JavaScript object
var location = new Object();
location.latitude = lat;
location.longitude = lng;
var altitude = new Object();
altitude.meters = alt;
altitude.feet = alt * 3.2808399;
Assemble response as a JSON object
var body = response.content.asJSON;
body.country = request.queryParams.country;
body.postalcode = request.queryParams.postalcode;
body.elevation = location;
body.altitude = altitude;
}
Handle any errors
catch (err) {response.content.asJSON.error = err;
}
Creating a JavaScript policy
Under apiproxy/policies, create a policy called MashItUp.xml of type JavaScript that references the JavaScript resource.
<JavaScript name="MashItUp" timeout="10000"> <ResourceURL>jsc://MashItUp.js</ResourceURL> </JavaScript>
Attaching a JavaScript policy to the ProxyEndpoint
Note that, unlike other API proxies, the TargetEndpoint definition is not invoked by the route rule. For this reason, the processing pipeline is agnostic about the existence of the apiproxy/targets/default.xml configuration that you have seen in other topics. The JavaScript app itself makes all of the necessary API calls.
<ProxyEndpoint name="default">
<Flows>
<Flow name="default">
<Request/>
<Response>
<!-- Call the JavaScript that invokes both APIs and returns a response -->
<Step><Name>MashItUp</Name></Step>
</Response>
</Flow>
</Flows>
<HTTPProxyConnection>
<!-- Add a base path to the proxy to distinguish from others in the environment -->
<BasePath>/weather</BasePath>
<VirtualHost>default</VirtualHost>
</HTTPProxyConnection>
<RouteRule name="default">
<!-- No route. The proxy will return the request and not call a backend service. -->
</RouteRule>
</ProxyEndpoint>
Importing and deploying the API proxy
Run the deploy tool in the base directory of the API platform samples distribution, substituting valid username and password for myname:mypass, and your Apigee organization for myorg.
$ python tools/deploy.py -n weatherapi -u myname:mypass -h https://api.enterprise.apigee.com -o myorg -e test -p / -d simpleProxy Writing simpleProxy/apiproxy/weatherapi.xml to apiproxy/weatherapi.xml Writing simpleProxy/apiproxy/policies/MashItUp.xml to apiproxy/policies/MashItUp.xml Writing simpleProxy/apiproxy/proxies/default.xml to apiproxy/proxies/default.xml Writing simpleProxy/apiproxy/resources/jsc/MashItUp.js to apiproxy/resources/jsc/MashItUp.js Writing simpleProxy/apiproxy/targets/default.xml to apiproxy/targets/default.xml Imported new proxy version 28 Undeploying revision 1 in same environment and path: Environment: test Revision: 2 BasePath = / State: deployed
Testing the composite service
$ curl http://myorg-test.apigee.net/weather?"postalcode=94306&country=us"
Sample Response
{"country":us,"postalcode":29407,"elevation":{"latitude":32.8017595,"longitude":-79.99759159999999},"altitude":{"meters":0.758330225944519,"feet":2.4879600626547935}}
Creating an API console for your new API
The composite service exposes an API, enabling Set up an Apigee API Console for sharing with other developers or for reuse across projects. A sample WADL file below describes the API, along with the appropriate Apigee WADL extensions. This Apigee-specific extensions control how the AIP Console displays the API. The WADL content below can be pasted into a file called elevation.wadl.xml.
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:apigee="http://api.apigee.com/wadl/2010/07/"
xmlns="http://wadl.dev.java.net/2009/02"
xsi:schemaLocation="http://wadl.dev.java.net/2009/02 \
http://apigee.com/schemas/wadl-schema.xsd \
http://api.apigee.com/wadl/2010/07/ \
http://apigee.com/schemas/apigee-wadl-extensions.xsd">
<resources base="http://{org_name}-test.apigee.net/v1">
<resource path="weather">
<method id="getGeocodedElevation" name="GET" apigee:displayName="Get Geocoded Elevation">
<apigee:tags>
<apigee:tag primary="true">Weather</apigee:tag>
</apigee:tags>
<apigee:authentication required="false"/>
<apigee:example url="weather"/>
<doc apigee:url="http://apigee.com/docs/api/content/building-composite-service-2-JavaScript-app">Return geocoded elevation data based on postal code and country code.</doc>
<request>
<param name="postalcode" type="xsd:string" style="query" default=""/>
<param name="country" type="xsd:string" style="query" default=""/>
</request>
</method>
</resource>
</resources>
</application>
To generate a dedicated API Console, upload this file using the Console To-Go tool. (A free Console To-Go account is required.)
Once the API Console is generated, it can be used to make requests to the composite service, and to view responses generated by the composite service.
Post questions to the Apigee Developer Forum.
Back to API Platform Developer Guide.