Managing Endpoints

Endpoints are custom REST services that are accessible from custom widgets or external systems. Through endpoints, custom widgets can access external services via HTTP.

In this case, endpoints act as a kind of HTTP proxy, so custom widgets can make Ajax requests to external services without having cross-domain issues. Also, endpoints can expose Application Builder's data sources, such as oneWEX collections, to external systems.

Endpoints are defined based on data source definitions. Therefore, all you need to do to enable endpoints is to create data sources.

The enableEndpoint property in the data source definition controls whether to enable endpoints for the data source. If you create a new data source, the enableEndpoint property is true by default.

An endpoint is accessible from the following URL where <datasource-id> is an id of a data source. Note that calls to endpoints must be authenticated.
https://your.server.com/builder/api/endpoint/<datasource-id>

Below is an example of a data source definition.

{
  "key": "my_ds",
  "name": "Book Search API",
  "enableEndpoint": true,
  "options": {
    "protocol": "https:",
    "method": "GET",
    "host": "external.server.com",
    "path": "/books/v1/search?start=${start}&max=${count}&q=${query}"
  },
  "map": {
    ...
  }
}

Abstract Proxy mode

If the base URL ends with a data source id (without a trailing slash) as shown in the example below, the endpoint serves in abstract proxy mode.
https://your.server.com/builder/api/endpoint/my_ds?start=0&count=10&query=cat

In this mode, a destination URL is constructed in the same way as the search API. That is, options.path in the data source definition is used to construct the URL and parameters are converted to actual ones based on the pattern in options.path.

In the abstract proxy mode, the result is normalized based on the field mapping in the data source definition, just like the search API. The result should look like this:

{
  "items": [ ... ],
  "itemsRaw": [ ... ],
  "totalCount": 489
}

Direct Proxy Mode

If a data source id is followed by an additional path as shown in the example below, endpoint works in the direct proxy mode.
https://your.server.com/builder/api/endpoint/my_ds/books/v1/search?max=10&q=cat

In this mode, a destination URL is constructed by appending the additional path to options.host in the data source definition. In this case, options.path is not used. The given parameters (?max=10&q=cat) are passed to the destination as they are.

The resulting data is never processed. The original data is returned with the original content-type. Any resources including images, XML etc. can be accessed in direct proxy mode.

Making Requests

The client can make a request with any of the following methods:

  1. GET request with query parameters.
  2. POST request with x-www-form-urlencoded parameters.
  3. POST request with JSON parameters.

Data source supports the same three types of REST services. All types of services can be called with any method. For example, a REST service that accepts POST requests with JSON parameters can be accessed using the GET request with query parameters.

CORS Support

When you wish to access an endpoint from an external web application, you may want to make Ajax requests to the endpoint. In general, the browser does not allow this since Application Builder and your external web application are in different domains. To overcome the browser's same origin policy, the Application Builder's endpoint feature supports Cross Origin Resource Sharing (CORS). It is enabled by default, and no special setup is required. The code below is an example of making Ajax requests to an endpoint using jQuery.

var url = "https://your.server.com/builder/api/endpoint/voc?start=0&count=10&query=juice";
$.ajax({
  url: url,
  type: 'GET',
})
.done(function(data) {
  data.items.forEach(function(item) {
    console.log(item.title);
  });
});

=>
apple juice
orange juice
...

Server-side scripts

You may want to customize payload at the server-side rather than client-side. For example, converting an XML response from backend to JSON, merging two response data into one, filter out unnecessary data to slim down the payload size, etc. In such cases, you can write custom JavaScript code that is executed by Node.js at the server-side. To do so, you can implement the applyEndpointFilter method in a data source adapter.

applyEndpointFilter method
Method
applyEndpointFilter: function(response: any, req: Request): any
Parameters
Table 1. Summary
Name Type Description
response any A response from the corresponding data source. It contains body and headers.
req Request A request object for the endpoint call.
Returns
Customized response.
Example (Manipulating JSON object)
You can customize body and headers and return the customized response. When the content type is application/json, for example, body would be a JSON string. You may want to use JSON.parse() to handle it as JavaScript object as shown in the example code below. You do not need to care about content-length as it will automatically be filled before sending the response to the client.
applyEndpointFilter: function(response, req) {
  var body = JSON.parse(response.body);
  // ... customize the body object as you like ...
  response.body = JSON.stringify(body);
  response.headers['x-custom-header'] = 'something';
  return response;
}
Example (Convert XML to JSON)

The example below is a case where data source response is an XML and you want to convert it to JSON. If you would like, you can utilize XML to JSON converter called xml-js. (https://www.npmjs.com/package/xml-js)

The variable convert, which holds a reference to xml-js, is accessible from the applyEndpointFilter method. Therefore, you can call convert.xml2js() to convert XML to a JavaScript object, as shown in the example below.
applyEndpointFilter: function(response, req) {
  try {
    var obj = convert.xml2js(response.body, {compact:true, spaces:4});
    // ... customize the json object as you like ...
    response.body = JSON.stringify(obj);
    response.headers['content-type'] = 'application/json; charset=UTF-8';
  } catch (e) {
    console.log(e);
  }
  return response;
}
Example (Asynchronous Operation)
If you want to perform asynchronous operation in the method, you can return a promise that resolves when the new response data is ready.
applyEndpointFilter: function(response, req) {
  var url = 'http://...';
  return new Promise((resolve, reject) => {
    fetch(url).then(function(response) {
      return response.text();
    }).then(function(text) {
      // your code goes here
      response.body = ...;
      resolve(response);
    });
  });
}