Fetch data from a REST API in an Oracle JET web app
Introduction
This tutorial shows you how to access a REST service, integrate it into your Oracle JavaScript Extension Toolkit (Oracle JET) web app, and bind data to a list view in your user interface.
Objectives
In this tutorial, you will learn how to create instances of the RESTDataProvider class. This class represents data available from JSON-based REST services.
Prerequisites
-
A development environment set up to create Oracle JET web apps that includes an installation of Node.js
-
(Option 1) Completion of the final tutorial in the previous learning path in this series: Handle Selection Events in an Oracle JET Web Application
-
(Option 2) If you haven’t completed the previous learning path in this series: the jet_web_application_temp.zip downloaded
Task 1: Download the Starter App
Skip this task if you’re continuing to work in an app that you created in the previous learning path.
-
Rename
jet_web_application_temp.zipasJET_Web_Application.zip. Extract the contents to theJET_Web_Applicationfolder. -
Navigate to the
JET_Web_Applicationfolder, and restore the Oracle JET web app.npm installThe app is ready to use.
Task 2: Access the REST Service
Click the Apex link to view the REST data for the Activities resource endpoint.
The data contains a list of activities with various attributes.
{
"items": [
{
"id": 1,
"name": "Baseball",
"short_desc": "Equipment we carry for baseball players.",
"image": "css/images/product_images/baseball.jpg"
},
. . .
],
"hasMore": false,
"limit": 25,
"offset": 0,
"count": 4,
"links": [
{
"rel": "self",
"href": "https://oracleapex.com/ords/oraclejet/lp/activities/"
},
. . .
]
}
Familiarize yourself with the data and the properties that the endpoint returns. You’ll need to understand these details when you create an instance of RESTDataProvider later in this tutorial. Note, for example, how the endpoint returns an items property that references a series of individual activities.
Task 3: Create a Data Provider to Fetch Activity Data in the ViewModel
-
Navigate to the
JET_Web_Application/src/ts/viewModelsdirectory and open thedashboard.tsfile in an editor. -
At the start of the
dashboard.tsfile, import theRESTDataProviderclass.import * as AccUtils from "../accUtils"; . . . import { RESTDataProvider } from "ojs/ojrestdataprovider"; . . . type Activity = { id: number; }; . . . type ActivityItems = { id: number; name: string; items: Array<Item>; short_desc: string; image: string; }; class DashboardViewModel { . . .Note: If you did not complete the previous learning path in this series, you also need to declare a type alias for
Activity, which is the type that theactivityDataProviderinstance in the next step requires. You specify this type alias before theDashboardViewModelclass declaration. -
In the
DashboardViewModelclass, declare the following variables and comment out or delete the preexistingactivityDataProviderof typeMutableArrayDataProvider<Activity["id"], Activity>.. . . class DashboardViewModel { // Activity key attribute that you'll pass as a parameter when creating // RESTDataProvider instance keyAttributes = "id"; // REST endpoint that returns Activity data restServerURLActivities = "https://oracleapex.com/ords/oraclejet/lp/activities/"; // RESTDataProvider instance activityDataProvider: RESTDataProvider<Activity["id"], Activity>; // MutableArraryDataProvider that we no longer use, and // so we comment it out. // activityDataProvider: MutableArrayDataProvider<Activity["id"], Activity>; -
In the
constructor()method of theDashboardViewModelclass, comment out or delete the preexistingactivityDataProvidervariable from theMutableArrayDataProviderclass and create theRESTDataProviderinstance.. . . class DashboardViewModel { . . . restServerURLActivities = "https://oracleapex.com/ords/oraclejet/lp/activities/"; activityDataProvider: RESTDataProvider<Activity["id"], Activity>; . . . constructor() { . . . // this.activityDataProvider = new MutableArrayDataProvider<Activity["id"], // Activity>(JSON.parse(storeData), { // keyAttributes: "id", // }); this.activityDataProvider = new RESTDataProvider({ keyAttributes: this.keyAttributes, url: this.restServerURLActivities, transforms: { fetchFirst: { request: async (options) => { const url = new URL(options.url); const { size, offset } = options.fetchParameters; url.searchParams.set("limit", String(size)); url.searchParams.set("offset", String(offset)); return new Request(url.href); }, response: async ({ body }) => { const { items, totalSize, hasMore } = body; return { data: items, totalSize, hasMore }; }, }, }, }); . . .Note: The
responsefunction above extracts data and other properties from the endpoint response body and must return an object with adataproperty. Given that the endpoint we work with returns anitemsproperty, we assign this latter property todatain theresponsefunction.
Task 4: Create a Data Provider to Fetch Item Data in the ViewModel
You use another RESTDataProvider instance to fetch a subset of the data that is the list of items for a particular activity. You do this by supplying a new URL that contains the selected activity ID. You’ll create an initial placeholder RESTDataProvider instance to use until such time as a user selects an activity, at which point you’ll create the actual RESTDataProvider instance in the selectedActivityChanged event listener method.
-
In the
DashboardViewModelclass of the opendashboard.tsfile, initialize a placeholder instance of theRESTDataProviderfor activity items, declare types for the additional variables needed to create the REST endpoint to return activity item data, and comment out or delete both theitemsDataProviderdeclaration of typeMutableArrayDataProvider<Item["id"], Item>and theitemsDataProvidervariable itself in theconstructor()method.class DashboardViewModel { // Activity key attribute that you'll pass as a parameter when creating // RESTDataProvider instance keyAttributes = "id"; // REST endpoint that returns Activity data restServerURLActivities = "https://oracleapex.com/ords/oraclejet/lp/activities/"; // RESTDataProvider instance activityDataProvider: RESTDataProvider<Activity["id"], Activity>; // Initialize activityKey to 3 to construct an initial REST call activityKey: number = 3; // Initialize placeholder RESTDataProvider itemsDataProvider: RESTDataProvider<Item["id"], Item> = new RESTDataProvider< Item["id"], Item >({ keyAttributes: "id", url: "", transforms: { fetchFirst: { request: null!, response: (): any => { return { data: [] }; }, }, }, }); // REST endpoint that returns Item data restServerURLItems = "https://oracleapex.com/ords/oraclejet/lp/activities/" + this.activityKey + "/items/"; constructor() { . . . // this.itemsDataProvider = new MutableArrayDataProvider<Item["id"], Item>( // itemsArray, // { keyAttributes: "id" } // ); -
In the
selectedActivityChangedmethod of theDashboardViewModelclass, write code to create a new instance of theitemsDataProvidervariable with the new activity items when a new activity is selected.This
itemsDataProvideralso includes a text filter to filter on the activity itemnamefield.selectedActivityChanged = ( event: ojListView.firstSelectedItemChanged<ActivityItems['id'], ActivityItems> ) => { /** * If no items are selected then the firstSelectedItem property returns an object * with both key and data properties set to null. */ let itemContext = event.detail.value.data; if (itemContext != null) { // If selection, populate and display list // Hide currently-selected activity item this.activitySelected(false); this.activityKey = event.detail.value.data.id; this.restServerURLItems = 'https://oracleapex.com/ords/oraclejet/lp/activities/' + this.activityKey + '/items/'; // Create the itemsDataProvider instance of RESTDataProvider this.itemsDataProvider = new RESTDataProvider({ keyAttributes: this.keyAttributes, capabilities: { filter: { textFilter: true, }, }, url: this.restServerURLItems, textFilterAttributes: ['name'], transforms: { fetchFirst: { request: async (options) => { const url = new URL(options.url); const { size, offset } = options.fetchParameters; url.searchParams.set('limit', String(size)); url.searchParams.set('offset', String(offset)); const filterCriterion = options.fetchParameters.filterCriterion as TextFilter<Item>; const { textFilterAttributes } = options.fetchOptions; if (filterCriterion && filterCriterion.text && textFilterAttributes) { const { text } = filterCriterion; textFilterAttributes.forEach((attribute) => { url.searchParams.set(attribute, text); }); } return new Request(url.href); }, response: async ({ body }) => { const { items, totalSize, hasMore } = body; return { data: items, totalSize, hasMore }; }, }, }, }); // Set List View properties this.activitySelected(true); this.itemSelected(false); this.selectedItem(); this.itemData(); } else { // If deselection, hide list this.activitySelected(false); this.itemSelected(false); } }; -
At the start of the
dashboard.tsfile, import theTextFilterinterface.import * as AccUtils from "../accUtils"; . . . import { RESTDataProvider } from "ojs/ojrestdataprovider"; import { TextFilter } from "ojs/ojdataprovider"; . . .Your file should look similar to final-dashboard-rest-dp-ts.txt.
Task 5: Add an Error Handler to Manage a Failure to Fetch Data
The RESTDataProvider instance provides an error option that you can use to invoke a callback function when an attempt to fetch data fails. You’ll implement this capability for the scenario where an attempt to fetch the list of activities fails.
-
Navigate to the
JET_Web_Application/src/ts/viewModelsdirectory and open thedashboard.tsfile in an editor. -
In the
DashboardViewModelclass, before theconstructor()method, add an observable for the status of the data fetch, plus a variable to reference an error message.class DashboardViewModel { . . . firstSelectedItem = ko.observable(); dataFetched = ko.observable(true); fetchError: string = (''); constructor() { . . . -
In the
activityDataProvidervariable that referencesRESTDataProvider, add theerroroption and a reference to the callback function that it invokes (fetchErrorHandler).this.activityDataProvider = new RESTDataProvider({ keyAttributes: this.keyAttributes, url: this.restServerURLActivities, error: this.fetchErrorHandler, transforms: { fetchFirst: { . . . -
After the
constructor()method of theDashboardViewModelclass, write code for thefetchErrorHandlercallback function that theRESTDataProviderinvokes if an attempt to fetch data fails.. . . } // end constructor fetchErrorHandler = (errorDetail: RESTDataProvider.FetchErrorDetail<number, Activity> | RESTDataProvider.FetchResponseErrorDetail<number, Activity>) => { this.dataFetched(false); if (errorDetail.hasOwnProperty('response')) { this.fetchError = `${(errorDetail as RESTDataProvider.FetchResponseErrorDetail<number, Activity>).response.status}`; } else { this.fetchError = (errorDetail as RESTDataProvider.FetchErrorDetail<number, Activity>).error.message; } } . . . -
Save the
dashboard.tsfile.Your
dashboard.tsfile should look similar to final-dashboard-error-handler-ts.txt. -
Navigate to the
JET_Web_Application/src/ts/viewsdirectory and open thedashboard.htmlfile in an editor. -
Find the
divelement whereid="parentContainer1"and wrap it with anoj-bind-ifcustom HTML element with thetestattribute set to the state of thedataFetched()observable.. . . <h1>Product Information</h1> <oj-bind-if test="[[dataFetched()]]"> <div id="parentContainer1" class="oj-flex oj-flex-init"> . . . </div> </oj-bind-if> -
Before the last
divelement in thedashboard.htmlfile add anotheroj-bind-ifcustom HTML element with thetestattribute set to the state of thedataFetched()observable.This newer
oj-bind-ifcustom HTML element wraps the HTML elements to display if theRESTDataProviderfailed to fetch data. In our example, we render a<p>HTML element with a short message for the end user.. . . <oj-bind-if test="[[!dataFetched()]]"> <p>Sorry that we couldn't get your product information right now. Please contact your system administrator.</p> </oj-bind-if> </div> -
Save the
dashboard.htmlfile.Your
dashboard.htmlfile should look similar to final-dashboard-error-handler-html.txt.
Task 6: Test the Web App
-
In the terminal window, change to the
JET_Web_Applicationdirectory and run the web app.npx ojet serve -
In the browser window, view the dynamic changes in your web app.

-
Close the browser window or tab that displays your running web app.
-
In the terminal window, press Ctrl+C, and if prompted, enter
yto exit the Oracle JET tooling batch job. -
In the terminal window, run the web app using the following additional command-line arguments.
npx ojet serve --server-port=8144 --livereload-port=8145On this occasion, the web app displays the following message because the REST service that it tries to access only accepts requests on the server port that the
ojet servecommand uses by default (8000), so the attempt by theRESTDataProviderto fetch from the REST service failed.Sorry that we couldn't get your product information right now. Please contact your system administrator.
Next Step
Proceed to the next tutorial in this module.
This tutorial is part of the module CRUD Operations Using a REST Service.
- Fetch Data from a REST API in an Oracle JET Web App
- Create a Form to Create Data Records in an Oracle JET Web App
- Update Data Records in an Oracle JET Web App
- Delete Data Records in an Oracle JET Web App
You can return to the learning path’s main page to access all the modules on building web apps.
More Learning Resources
Explore other labs on docs.oracle.com/learn or access more free learning content on the Oracle Learning YouTube channel. Additionally, visit education.oracle.com/learning-explorer to become an Oracle Learning Explorer.
For product documentation, visit Oracle Help Center.
Fetch data from a REST API in an Oracle JET web app
F11593-13