Fetch API series:


Fetch is a JavaScript API native to all browsers that allows you to make a request to a URL. That request returns a response, which you can then use any way you want.

Without passing any option to the Fetch API, the following code is implicitly making a GET request to a URL:

const request = fetch('https://something.com/i/need/right/now')
Simple fetch

Try my demo app to see 3 different ways of displaying the same data. You'll have to look the browser developer tools network tab to understand what's going on.

Why is it relevant for Oracle APEX development?

I've been slowly adopting a new strategy regarding storing my SQL queries in Oracle APEX. Typically I used to have SQL directly in APEX regions like this:

SQL directly in region source

Having the SQL in the region is simple, but it comes at the expanse of reusability. I'm not talking about wrapping SQL in views. That too, we should do as much as possible.

I'm talking about moving the SQL to ORDS. Why?

  • APEX can consume it as REST Data Source, so you really only write the SQL once and can use it in multiple regions.
  • SQL is now exposed for REST consumption, with the appropriate security and authentication, allowing easy data sharing.
  • REST enabled data allows using the Fetch API. This is was this blog post is about

Using the Fetch API in Oracle APEX

Here's a simple step by step tutorial on how to leverage the Fetch API in Oracle APEX. These are just the building blocks, you could wrap some of the following code in files.

Step 1: Move your SQL to ORDS

Creating a GET handler for the same query we used in the APEX Card Report above

For simplicity, I'm skipping the ORDS authentication so this is a public unsecured endpoint. I may do a separate blog post about how to do that.

This endpoint is publicly available so you can fetch it yourself with:

fetch('https://apex.oracle.com/pls/apex/vmorneau/eba/projects')
*this line only works if you are fetching from https://apex.oracle.com due to CORS

Step 2: Create an empty Static Content region

We are going to load the data from the ORDS endpoint into an empty region, so we first have to create a Static Content region on our APEX page and give it a static ID.

Static Content region with ID #RegionFetchAPI

Step 3: Fetch the ORDS endpoint

// Fetches an ORDS endpoint (GET)
const request = await fetch('/pls/apex/vmorneau/eba/projects')
// Turns the request into readable text
const response = await request.text();
// Turns into a JSON object for easier manipulation of the data
const data = JSON.parse(response);

Step 4: Creating a card template for our data

The following code is a pure copy and paste from the APEX - Shared Components - Templates - Cards (Report) that I put into 3 JavaScript variables (before rows, row, after rows).

// Design the card template we want to build
const templateBeforeRows = `<ul class="t-Cards u-colors t-Cards--featured t-Cards--block force-fa-lg t-Cards--displayIcons t-Cards--5cols t-Cards--animColorFill t-Cards--hideBody">`;
const template = `
<li class="t-Cards-item #CARD_MODIFIERS#">
  <div class="t-Card">
    <div class="t-Card-wrap">
      <div class="t-Card-icon u-color #CARD_COLOR#"><span class="t-Icon fa #CARD_ICON#"><span class="t-Card-initials" role="presentation">#CARD_INITIALS#</span></span></div>
      <div class="t-Card-titleWrap"><h3 class="t-Card-title">#CARD_TITLE#</h3><h4 class="t-Card-subtitle">#CARD_SUBTITLE#</h4></div>
      <div class="t-Card-body">
        <div class="t-Card-desc">#CARD_TEXT#</div>
        <div class="t-Card-info">#CARD_SUBTEXT#</div>
      </div>
      <span class="t-Card-colorFill u-color #CARD_COLOR#"></span>
    </div>
  </div>
</li>
`;
const templateAfterRows = `</ul>`;

Step 5: Apply the template to the data

// Append the Cards wrapper (before and after rows) to a region on our page
apex.jQuery("#RegionFetchAPI .t-Region-body").append(templateBeforeRows);
apex.jQuery("#RegionFetchAPI .t-Region-body").append(templateAfterRows);

// Append the rows coming from ORDS
data.items.forEach(item => {
  let options = { placeholders: getPlaceholders(item) };
  let result = apex.util.applyTemplate(template, options);
  apex.jQuery("#RegionFetchAPI .t-Region-body .t-Cards").append(result);
});

Putting it all together

// Fetches an ORDS endpoint (GET)
const request = await fetch('/pls/apex/vmorneau/eba/projects')
// Turns the request into readable text
const response = await request.text();
// Turns into a JSON object for easier manipulation of the data
const data = JSON.parse(response);

// Design the card template we want to build
const templateBeforeRows = `<ul class="t-Cards u-colors t-Cards--featured t-Cards--block force-fa-lg t-Cards--displayIcons t-Cards--5cols t-Cards--animColorFill t-Cards--hideBody">`;
const template = `
<li class="t-Cards-item #CARD_MODIFIERS#">
  <div class="t-Card">
    <div class="t-Card-wrap">
      <div class="t-Card-icon u-color #CARD_COLOR#"><span class="t-Icon fa #CARD_ICON#"><span class="t-Card-initials" role="presentation">#CARD_INITIALS#</span></span></div>
      <div class="t-Card-titleWrap"><h3 class="t-Card-title">#CARD_TITLE#</h3><h4 class="t-Card-subtitle">#CARD_SUBTITLE#</h4></div>
      <div class="t-Card-body">
        <div class="t-Card-desc">#CARD_TEXT#</div>
        <div class="t-Card-info">#CARD_SUBTEXT#</div>
      </div>
      <span class="t-Card-colorFill u-color #CARD_COLOR#"></span>
    </div>
  </div>
</li>
`;
const templateAfterRows = `</ul>`;

// Append the Cards wrapper (before and after rows) to a region on our page
apex.jQuery("#RegionFetchAPI .t-Region-body").append(templateBeforeRows);
apex.jQuery("#RegionFetchAPI .t-Region-body").append(templateAfterRows);

/**
 * Turns an object coming from an ORDS request into a usable placeholder for apex.util.applyTemplate
 * @param {object} item - Object from an ORDS request
 **/
const getPlaceholders = item => {
  let placeholders = {};

  for (let key in item) {
    placeholders[key.toUpperCase()] = item[key];
  }
  
  return placeholders;
};

// Append the rows coming from ORDS
data.items.forEach(item => {
  let options = { placeholders: getPlaceholders(item) };
  let result = apex.util.applyTemplate(template, options);
  apex.jQuery("#RegionFetchAPI .t-Region-body .t-Cards").append(result);
});

Finally, why do all this?

First of all, yes, you should use the built in APEX's REST Data Sources to display your card report from an ORDS endpoint.

For me, I do a lot of client-side data and UI manipulation and I find the fetch & applyTemplate combo so powerful. It allows my apps to make much fewer page submits which mean fewer page loads. It's all about partial data refresh, and making your apps feel snappier.

So this is how I like to work these days, but perhaps I'll change my mind next week. Who knows.