Fetch data from a REST API in an Oracle JET virtual DOM app
Introduction
This tutorial shows you how to access a REST service, integrate it into your Oracle JavaScript Extension Toolkit (Oracle JET) virtual DOM 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 virtual DOM 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 Virtual DOM App
- (Option 2) If you haven’t completed the previous learning path in this series: the jet-virtual-dom-app-temp.zip downloaded
Task 1: Download the Starter Virtual DOM App
Skip this task if you’re continuing to work in an app that you created in the previous learning path.
-
Rename
jet-virtual-dom-app-temp.zipasJET-Virtual-DOM-app.zip. Extract the contents to theJET-Virtual-DOM-appdirectory. -
Navigate to the
JET-Virtual-DOM-appdirectory, and restore the Oracle JET virtual DOM app.npm installThe virtual DOM 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
-
Navigate to the
JET-Virtual-DOM-app/src/components/directory and open theParentContainer1.tsxfile in an editor. -
At the start of the
ParentContainer1.tsxfile, import theRESTDataProvidermodule and delete or comment out the import statements for theMutableArrayDataProvidermodule and thestore_data.jsonfile.We also import the
useMemohook that we’ll use when creating theRESTDataProviderlater.import { h } from "preact"; . . . // import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider"); // import * as storeData from "text!./store_data.json"; import { RESTDataProvider } from "ojs/ojrestdataprovider"; import { useState, useMemo } from "preact/hooks"; . . . -
Create a
keyattributesvariable and arestServerURLActivitiesvariable that references the activity key attribute and the REST endpoint that you’ll pass to theRESTDataProviderinstance that you’ll create in the next step.let keyAttributes: string = 'id'; // REST endpoint that returns Activity data const restServerURLActivities: string = 'https://oracleapex.com/ords/oraclejet/lp/activities/'; -
Create a new
activityDataProvidervariable that references theRESTDataProvidermodule and delete or comment out the preexistingactivityDataProvidervariable that referenced theMutableArrayDataProvidermodule.We create the new
activityDataProvidervariable within theParentContainer1function and we wrap it within auseMemohook to ensure that the data provider instance is re-created only if the data in the data provider actually changes.const ParentContainer1 = () => { const activityDataProvider = useMemo(() => new RESTDataProvider<Activity["id"], Activity>({ keyAttributes: keyAttributes, url: 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 that extracts data and other properties from the endpoint response body must return an object with adataproperty. Given that the endpoint we work with returns anitemsproperty, we assign this latter property todatain the response function. -
Save the
ParentContainer1.tsxfile.Your
ParentContainer1.tsxfile should look similar to ParentContainer1-a.tsx.txt. -
Navigate to the
JET-Virtual-DOM-app/src/components/Activitydirectory and open theActivityContainer.tsxfile in an editor. -
At the start of the
ActivityContainer.tsxfile, import theRESTDataProvidermodule and comment out or delete the import statement for theMutableArrayDataProvidermodule.import { h, ComponentProps } from "preact"; . . . // import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider"); import { RESTDataProvider } from "ojs/ojrestdataprovider"; . . . -
In the
Propstype alias, modify the optionaldataproperty to reference theRESTDataProvidertype instead of the preexisting typeMutableArrayDataProvider<Activity["id"], Activity>.type Props = { data?: RESTDataProvider<Activity["id"], Activity>; // data?: MutableArrayDataProvider<Activity["id"], Activity>; . . . }; -
Save the
ActivityContainer.tsxfile.Your
ActivityContainer.tsxfile should look similar to ActivityContainer.tsx.txt.
Task 4: 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.
-
In the
ParentContainer1.tsxfile from theJET-Virtual-DOM-app/src/components/directory, import theuseRefhook from Preact.. . . import { RESTDataProvider } from "ojs/ojrestdataprovider"; import { useState, useMemo, useRef } from "preact/hooks"; . . . -
In the
activityDataProvidervariable that referencesRESTDataProvideradd theerroroption and a reference to the callback function that it invokes (fetchErrorHandler).const ParentContainer1 = () => { const activityDataProvider = useMemo(() => new RESTDataProvider<Activity["id"], Activity>({ keyAttributes: keyAttributes, url: restServerURLActivities, error: fetchErrorHandler, transforms: { . . . -
Before the
activityDataProvidervariable, add the code forfetchErrorHandlerand the hooks (useStateanduseRef) that we use to determine if the attempt to fetch data succeeded.. . . const ParentContainer1 = () => { const [fetchStatus, setFetchStatus] = useState(true); const fetchError = useRef<string>(); const fetchErrorHandler = (errorDetail: RESTDataProvider.FetchErrorDetail<number, Activity> | RESTDataProvider.FetchResponseErrorDetail<number, Activity>) => { setFetchStatus(false); if (errorDetail.hasOwnProperty('response')) { fetchError.current = `${(errorDetail as RESTDataProvider.FetchResponseErrorDetail<number, Activity>).response.status}`; } else { fetchError.current = (errorDetail as RESTDataProvider.FetchErrorDetail<number, Activity>).error.message; } } const activityDataProvider = new RESTDataProvider<Activity["id"], Activity>({ . . . -
In the return statement at the end of the the
ParentContainer1.tsxfile, add a check that determines whether you display the list of activities or a message in the case where the attempt to fetch data failed.. . . return ( <div> {fetchStatus ? ( <div id="parentContainer1" class="oj-flex oj-flex-init"> <ActivityContainer data={activityDataProvider} onActivityChanged={activityChangedHandler} /> {showActivityItems() && (<ParentContainer2 activity={selectedActivity} />)} {!showActivityItems() && (<h4 class="oj-typography-subheading-sm">Select activity to view items</h4>)} </div>) : (<p>Sorry that we couldn't get your product information right now. Please contact your system administrator.</p> )} </div> ); }; export default ParentContainer1; -
Save the
ParentContainer1.tsxfile.Your
ParentContainer1.tsxfile should look similar to ParentContainer1-b.tsx.txt.
Task 5: Create a Data Provider to Fetch Item Data
Use another RESTDataProvider instance to fetch a subset of the data, the list of items for a particular activity. You do this by supplying a new URL that contains the selected activity ID.
-
Navigate to the
JET-Virtual-DOM-app/src/components/directory and open theParentContainer2.tsxfile in an editor. -
At the start of the
ParentContainer2.tsxfile, import theRESTDataProvidermodule and delete or comment out the import statements for theMutableArrayDataProvidermodule and thestore_data.jsonfile. Also import theTextFilterinterface that we’ll use when we enable the filtering capability in theRESTDataProviderinstance that we create.import { h } from "preact"; . . . import { RESTDataProvider } from "ojs/ojrestdataprovider"; import { TextFilter } from "ojs/ojdataprovider"; // import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider"); // import * as storeData from "text!./store_data.json"; . . . -
After the
Itemtype alias, create abaseServiceUrlvariable to reference the REST endpoint that you’ll pass to the instance of theRESTDataProviderthat you’ll create in the next step.type Item = { . . . }; const baseServiceUrl = "https://oracleapex.com/ords/oraclejet/lp/activities/"; -
Create an initial instance of the
RESTDataProviderthat you’ll pass in subsequent steps to theuseStateanduseEffectPreact hooks.const baseServiceUrl = 'https://oracleapex.com/ords/oraclejet/lp/activities/'; let INIT_DATAPROVIDER = new RESTDataProvider<ActivityItem['id'], ActivityItem>({ keyAttributes: 'id', url: baseServiceUrl, transforms: { fetchFirst: { request: null!, response: (): any => { return { data: [] }; }, }, }, }); -
Comment out or delete the pre-existing code that created a variable to read data from the
store_data.jsonfile and that created an initial instance of theMutableArrayDataProvider.// const activityData = JSON.parse(storeData); // let activityItemsArray = activityData[0].items; // // Create data provider instance for the array of activity items for the selected activity // const INIT_DATAPROVIDER = new MutableArrayDataProvider<ActivityItem["id"], ActivityItem>(activityItemsArray, { // keyAttributes: "id", // }) -
In the
ParentContainer2function, replace the existinguseEffecthook that manages the instance ofMutableArrayDataProviderwith a new definition that creates aRESTDataProviderfor the activity items that corresponds to the selected activity ID. This new definition also includes a text filter to filter on the activity itemnamefield.const ParentContainer2 = (props: Props) => { . . . useEffect(() => { setactivityItemDP( new RESTDataProvider<ActivityItem["id"], ActivityItem>({ keyAttributes: "id", capabilities: { filter: { textFilter: true, }, }, url: baseServiceUrl + "/" + props.activity?.id + "/items/", 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 }; }, }, }, }) ); }, [props.activity]); return ( . . . -
Save the
ParentContainer2.tsxfile.Your
ParentContainer2.tsxfile should look similar to ParentContainer2.tsx.txt. -
Navigate to the
JET-Virtual-DOM-app/src/components/ActivityItemdirectory and open theActivityItemContainer.tsxfile in an editor. -
At the start of the
ActivityItemContainer.tsxfile, import theRESTDataProvidermodule and comment or delete the import statement for theMutableArrayDataProvidermodule.import { h, ComponentProps } from "preact"; . . . // import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider"); import { RESTDataProvider } from "ojs/ojrestdataprovider"; . . . -
In the
Propstype alias, modify thedataproperty to reference theRESTDataProvidertype instead of the preexisting typeMutableArrayDataProvider<Activity["id"], Activity>.type Props = { // data?: MutableArrayDataProvider<ActivityItem["id"], ActivityItem>; data?: RESTDataProvider<ActivityItem['id'], ActivityItem>; selectedActivity: Item | null; onItemChanged: (item: Item) => void; }; -
Save the
ActivityItemContainer.tsxfile.Your
ActivityItemContainer.tsxfile should look similar to ActivityItemContainer.tsx.txt.
Task 6: Test the Virtual DOM App
-
In the terminal window, change to the
JET-Virtual-DOM-appdirectory and run the virtual DOM app.npx ojet serve -
In the browser window, view the dynamic changes in your virtual DOM app.

-
Close the browser window or tab that displays your running virtual DOM 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 virtual DOM app using the following additional command-line arguments.
npx ojet serve --server-port=8144 --livereload-port=8145On this occasion, the virtual DOM app displays the following message because the REST service that it trys to access only accepts requests on the server port that the
ojet servecommand uses by default (8000), so the attempt by the RESTDataProvider to 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 the REST API in Oracle JET
- Create a Form to Create Data Records in an Oracle JET Virtual DOM App
- Update Data Records in an Oracle JET Virtual DOM App
- Delete Data Records in an Oracle JET Virtual DOM App
You can return to the virtual DOM learning path’s main page to access all the modules on building virtual DOM 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 virtual DOM app
F70627-06