Tag: ords api

  • New ORDS feature: handling multiple files from a multipart/form-data POST request

    New ORDS feature: handling multiple files from a multipart/form-data POST request

    A new feature

    An ORDS user (or application) can now upload multiple files as part of a multipart/form-data POST request under various conditions. How can this be achieved with an ORDS endpoint?

    First, you must become acquainted with some newly introduced PL/SQL procedures and functions. You can find these in your ORDS Metadata.

    The single function and procedures used in this example.

    The code

    Now that you know where to look for these new procedures and functions, I’ll walk through my code.

    Resource Handler

    Here is the code I’m using for my Resource Handler (i.e., the ORDS API). It is a POST request that accepts the following parameters:

    • l_body_json
    • l_parameter_name
    • l_file_name
    • l_content_type
    • l_file_body

    Also, pay special attention to these three pieces of Resource Handler code:

    Line 2L_BODY_JSON CLOB := :BODY_JSON
    Lines 9 and 10ORDS.BODY_FILE_COUNT
    Line 11ORDS.GET_BODY_FILE
    These are new additions to ORDS; we’ll be revisiting them shortly.
    DECLARE
        L_BODY_JSON      CLOB := :BODY_JSON;
        L_PARAMETER_NAME VARCHAR2(4000);
        L_FILE_NAME      VARCHAR2(4000);
        L_CONTENT_TYPE   VARCHAR2(200);
        L_FILE_BODY      BLOB;
    
    BEGIN
        HTP.P( 'Number of files that were received: ' || ORDS.BODY_FILE_COUNT);
        FOR i IN 1..ORDS.BODY_FILE_COUNT LOOP
            ORDS.GET_BODY_FILE(
                P_FILE_INDEX     => i,
                P_PARAMETER_NAME => L_PARAMETER_NAME,
                P_FILE_NAME      => L_FILE_NAME,
                P_CONTENT_TYPE   => L_CONTENT_TYPE,
                P_FILE_BLOB      => L_FILE_BODY
            );
    
            INSERT INTO BODYFILESDEMO (
                FILENAME,
                CONTENTTYPE,
                FILEBODY
            ) VALUES ( L_FILE_NAME,
                       L_CONTENT_TYPE,
                       L_FILE_BODY );
            HTP.P('Inserted File: ' || L_FILE_NAME );
        END LOOP;
    
    END;

    Expand for this example’s complete PL/SQL ORDS Resource Module definition.
    -- Generated by ORDS REST Data Services 24.3.0.r2620924
    -- Schema: ORDSDEMO  Date: Fri Oct 04 07:28:00 2024 
    --
            
    BEGIN
      ORDS.DEFINE_MODULE(
          p_module_name    => 'body.files.demo',
          p_base_path      => '/v1/',
          p_items_per_page => 25,
          p_status         => 'PUBLISHED',
          p_comments       => NULL);
    
      ORDS.DEFINE_TEMPLATE(
          p_module_name    => 'body.files.demo',
          p_pattern        => 'example',
          p_priority       => 0,
          p_etag_type      => 'HASH',
          p_etag_query     => NULL,
          p_comments       => NULL);
    
      ORDS.DEFINE_HANDLER(
          p_module_name    => 'body.files.demo',
          p_pattern        => 'example',
          p_method         => 'POST',
          p_source_type    => 'plsql/block',
          p_mimes_allowed  => NULL,
          p_comments       => NULL,
          p_source         => 
    'DECLARE
        L_BODY_JSON      CLOB := :BODY_JSON;
        L_PARAMETER_NAME VARCHAR2(4000);
        L_FILE_NAME      VARCHAR2(4000);
        L_CONTENT_TYPE   VARCHAR2(200);
        L_FILE_BODY      BLOB;
    BEGIN
        HTP.P( ''Number of files that were received: '' || ORDS.BODY_FILE_COUNT);
        FOR i IN 1..ORDS.BODY_FILE_COUNT LOOP
            ORDS.GET_BODY_FILE(
                P_FILE_INDEX     => i,
                P_PARAMETER_NAME => L_PARAMETER_NAME,
                P_FILE_NAME      => L_FILE_NAME,
                P_CONTENT_TYPE   => L_CONTENT_TYPE,
                P_FILE_BLOB      => L_FILE_BODY
            );
    
            INSERT INTO BODYFILESDEMO (
                FILENAME,
                CONTENTTYPE,
                FILEBODY
            ) VALUES ( L_FILE_NAME,
                       L_CONTENT_TYPE,
                       L_FILE_BODY );
            HTP.P(''Inserted File: '' || L_FILE_NAME );
        END LOOP;
    
    END;');
    
        
            
    COMMIT;
    
    END;

    And here is the table I’m using:

    And the DDL, should you choose to recreate the table:

    CREATE TABLE BODYFILESDEMO 
        ( 
         ID          NUMBER (*,0) GENERATED BY DEFAULT AS IDENTITY 
            ( START WITH 1 CACHE 20 )  NOT NULL , 
         FILENAME    VARCHAR2 (200) , 
         CONTENTTYPE VARCHAR2 (200) , 
         FILEBODY    BLOB 
        ) 
    ;

    Practical example

    In practice, here is how everything works. I’m using Postman as a proxy for my application (i.e., client); I have it set up like this:

    Next, if I were to issue a POST request to my ORDS endpoint (using Postman), here are the results:

    In short, where before, I couldn’t INSERT multiple files via an ORDS POST request, now I can.

    Dissecting the POST

    Using the :body_json implicit parameter (you can review all the ORDS implicit parameters here), in concert with the ORDS.body_file_count function and the ORDS.get_body_file procedure, you can now send multipart/form-data with files (as seen in this POST request). You might also include JSON form data in the POST request body in these requests, but it is not compulsory.

    There are considerations though; read on.

    A closer look

    1. When POSTing a multipart/form-data request (such as the one in this example), I must bind this :BODY_JSON implicit parameter to something, even if that something will be an empty property (i.e., even if my POST request doesn’t have form data in JSON format).
    2. ORDS interprets this handler code as such:
      • ORDS knows to receive form data in JSON format; however, even if no form data is present, it knows that there may be multiple files in the POST request.
    3. And if there are numerous files, ORDS can use the ORDS.BODY_FILE_COUNT function to count how many files there are (using the FOR LOOP) and then with the ORDS.GET_BODY_FILE function perform the next operation, which in this case is an INSERT INTO table operation.
    DECLARE
        L_BODY_JSON      CLOB := :BODY_JSON;
        L_PARAMETER_NAME VARCHAR2(4000);
        L_FILE_NAME      VARCHAR2(4000);
        L_CONTENT_TYPE   VARCHAR2(200);
        L_FILE_BODY      BLOB;
    
    BEGIN
        HTP.P( 'Number of files that were received: ' || ORDS.BODY_FILE_COUNT);
        FOR i IN 1..ORDS.BODY_FILE_COUNT LOOP
            ORDS.GET_BODY_FILE(
                P_FILE_INDEX     => i,
                P_PARAMETER_NAME => L_PARAMETER_NAME,
                P_FILE_NAME      => L_FILE_NAME,
                P_CONTENT_TYPE   => L_CONTENT_TYPE,
                P_FILE_BLOB      => L_FILE_BODY
            );
    
            INSERT INTO BODYFILESDEMO (
                FILENAME,
                CONTENTTYPE,
                FILEBODY
            ) VALUES ( L_FILE_NAME,
                       L_CONTENT_TYPE,
                       L_FILE_BODY );
            HTP.P('Inserted File: ' || L_FILE_NAME );
        END LOOP;
    
    END;

    TL;DR implicit bind parameter review

    As far as a multipart/form-data with files POST request goes, ORDS “:BODY_" bind parameters now include and support the following:

    Impact Bind ParameterAbout files…About form data…About JSON…
    :BODYAccepts only one file (to be used with BLOB files).Multiple files are accessible using the new ORDS.BODY_FILE function and procedures.n/a
    :BODY_TEXTAccepts only one file (to be used with CLOB files).ORDS will automatically recognize form data and handle it accordingly.n/a
    :BODY_JSONORDS will automatically recognize form data and handle it accordingly.Form data will NOT be automatically recognized.Will be treated accordingly where form data, in JSON format, exists in the POST body.
    How ORDS now treats multipart/form-data with files POST requests under various conditions.

    One final note

    These changes only relate to multipart/form-data with files POST requests! Media types such as the following are not the focus of this article:

    • application/x-url-urlencoded
    • application/x-www-form-urlencoded
    • application/json, and
    • multipart/form-data without files

    The end

    That’s it for now. I’m sure there will be questions. I hope to put together a working example in a Flask app (a remix of our LiveLab workshop) soon. So stay tuned. I’ll update you as I learn more. Leave a comment if anything is unclear, and share if you think this is helpful.

    Follow

    And don’t forget to follow, like, subscribe, share, taunt, troll, or stalk me!

  • ORDS 24.2 Release Highlights

    ORDS 24.2 Release Highlights

    NOTE: This can be thought of as a "companion piece" to the official ORDS release notes 🤓.

    What is new in Oracle REST Data Services 24.2?


    New HTTPS Response Status Codes

    In response to various support requests and internal feedback, we've expanded on the existing ORDS Status Codes (the current list can be found here)!
    Why? The TL;DR is that in many cases, the error or exception caught was being "bucketed" into a category of codes, like a 400 Bad Request or a 500 Internal Service Error. While true, we can be more explicit with some of these error codes. Now, we just have to ask, at what level do we stop?!

    Ahem, this sounds like our entire dev team is lazy; it’s quite the opposite. Let me illustrate what’s actually going on with an example from one of our related tickets.

    Say a user receives a 503 Service Unavailable server error response. In their Java logs (trace files, access logs, etc.), they also observe an oracle.net.ns.NetException error. While classifying this as a 503 is accurate, we can do better. For instance, you may encounter an oracle.net.ns.NetException with any of the following ORA codes:

    ORA-12514 TNS: The listener does not currently know of the service requested in the connect descriptor
    ORA-12506 TNS: listener rejected connection based on service ACL filtering
    ORA-12529 TNS: connect request rejected based on current filtering rules
    ORA-12516, TNS: The listener could not find an available handler with a matching protocol stack

    In this example, moving forward, instead of receiving a 503, you’ll now receive a 571 server error response.

    We’ve done this for several other scenarios, too! Why keep everything generic, as a 503, when we can be more discrete and specific with the information provided? It makes no sense. So, by shifting some of these exceptions to unique response codes, we make it easier for you to identify what is happening.

    What if everything was either 200, 400, or 500? Could you imagine?! It would be nearly impossible to quickly identify what is happening in your stack.

    ORDS Central Configuration

    ORDS now supports deployment in a Central Configuration type deployment.

    This one is big. The short version is that we’ve made it so you can dynamically start up and shut down individual ORDS “nodes.” This requires three main pieces, but I’ll try to be brief.

    This all assumes you’ve already configured ORDS globally (i.e., you have a global.xml file) as well as your database pools (i.e., the default.xml, database_pool_one.xml, database_pool_two.xml, etc. files). But the idea is such that you’ll have stored in a central Vault/Secrets storage/Key Store your:

    1. ORDS global configuration – which will be in a JSON format (basically a JSON version of the global.xml file)
    2. ORDS database pool configuration/s – the individual configuration files for your ORDS “nodes” (again, just JSON versions of your database-pool.xml files)
    3. A mechanism for the ORDS_PUBLIC_USER to authenticate with the Vault or Keystore (this could also be something as simple as an ORDS webhook that has been protected with an OAuth2.0 client)

    *And a conditional fourth item, the makestore or orapki utilities (for creating SSO Wallets).

    One way to “do” this (we’ve tested this internally; we just can’t, and won’t endorse, a “one-size fits all” method) is to store your “secrets” in a vault-type service and retrieve them securely and dynamically.

    NOTE: The vault contains "secrets," including your global configuration and database pool files. Each of those secrets might be behind an OAuth 2.0-protected endpoint/s.

    Separately, and before starting up ORDS, you’d create an SSO Wallet with the credentials for the ORDS_PUBLIC_USER, its password, and additional hostname information (all can be found in our documentation). You’d also choose where to store that Wallet.

    Then, before launching ORDS, you’d include two Java options:

    1. Your wallet location (so when ORDS starts up, it is aware of the location of the Wallet and the credentials) and
    2. The REST endpoint (the URI) for your global configuration

    Authentication can take several forms here. You can use Basic Authentication (but…don’t, even though we support it, perhaps for testing, but please don’t use it for production), JWTs, or OAuth 2.0. Once the ORDS_PUBLIC_USER has authenticated with its credentials/token, ORDS can acquire its global configuration.

    When that HTTP request comes across ORDS, the database pool name can be passed as a header value (we can also read the database pool name if it is appended to the beginning of the URL) and used as a “search” value to retrieve the relevant database_pool-config.json file.

    That is ORDS Central Configuration in a nutshell. And since I lost you, I’ll have to write a follow-up blog on this. I don’t blame you; conceptually, it’s tough to envision, but it’s pretty simple in practice. The basic components are laid out in our documents.

    OCI Monitoring of ORDS

    ORDS now provides a create_alarms.sh script to create ORDS alarms using OCI Monitoring Services, which can be found in the [ords product folder]/examples/ords-metrics directory.

    How shall I explain this without getting in trouble with our legal department…? This is being rolled out globally right now for the Oracle Autonomous database. Before this ORDS release, errors or exceptions would be caught and streamed to an OCI metrics dashboard. And you might have seen an ORDS-related exception with a 404 or a 503. Unfortunately, the root-cause analysis would have already been off to a bad start. Because most of these exceptions aren’t just 404s or 503s. There had yet to be a mechanism to make some of these exceptions more discrete. And that is where this create_alarms.sh script enters the chat.

    If you review the now-included create_alarms.sh file, you can get an idea of what will now be streamed to your OCI Metrics/Monitoring Explorer. We’ve made it so the ORDS access logs and more appropriate response codes can be streamed to OCI. This makes root-cause analysis and troubleshooting far more straightforward. So if you elect to configure a customer-managed ORDS node, then be sure to take advantage of this new capability.

    A new standalone configuration option

    New standalone.access.log.retainDays configuration option for ORDS standalone. 

    Users can now customize the number of days before access log files are overwritten. The default is 90 days, but you can now select the exact number. This, along with numerous other standalone settings can be found in the Understanding Configurable Settings appendix of our ORDS Installation and Configuration Guide.

    Java options in the ORDS CLI

    Users may now include Java Options parameters when executing ORDS CLI commands. 

    For instance, a user may execute the following command:

    ords --java-options "-Djava.util.logging.config.file=logging.properties"

    Or, maybe you are testing the new ORDS Central Configuration strategy. In that case, you might want to include something like this when starting ORDS:

    ords --java-options "-Dconfig.url=//localhost.8080" serve

    In the above example, you’ve told ORDS to start up, and then, first thing, go to the target URL to retrieve ORDS’ global configuration settings.

    You may wonder about JAVA_OPTIONS and JDK_JAVA_OPTIONS; how are those settings impacted? Well, here are some essential details:

    1. ords --java-options only apply to the current ORDS execution.
    2. In order of precedence, options will be picked up like this:

    JAVA_OPTIONS are of the highest precedence >>> then >>> ords --java-options >>> then >>> JDK_JAVA_OPTIONS

    INFO: Where conflicts arise, ORDS will pick up and use the left-most option.

    Jetty 10.0.21

    ORDS standalone updates the embedded Jetty Web Sever version 10.0.21.

    There’s not much to say here, but if you’d like to learn more about Jetty, I’m including the Operations and Programming guides. When using ORDS in standalone mode, the idea is that Jetty is just there—and it just works.

    We’ve architected Standalone mode so that any ORDS configuration can be achieved via the ORDS CLI or by directly manipulating the XML configuration files (while you can do this, you shouldn’t; you might mess up one of the properties in the files). Designing ORDS Standalone mode this way makes it so you don’t really need to do anything Jetty-related. I’m just including the docs if you want something to read on your lunch break 🤪.

    Database Actions’ Data Pump

    Data Pump allows you to delete jobs and their files from within the Database Actions Data Pump UI. And the Data Pump Import Wizard features some major upgrades and visual enhancements. 

    Users who have DBMS credentials to access Oracle Cloud Services (via the DBMS_CLOUD.CREATE_CREDENTIAL PL/SQL procedure) can now perform Data Pump Imports from OCI Buckets (previously, it was Resource Principals only). And we’ve added the following abilities/changes:

    1. Buckets from any Compartment level are now accessible
    2. Auto-generated Import Patterns for DMP Files are now included
    3. Automatic mapping for Schemas and Tablespaces is now present
    4. A new “Append Timestamp to Log and Job Names” toggle option has been added

    NDJSON files

    Users can now import Newline Delimited JSON (NDJSON) or .ndjson files into the SQL Worksheet.

    This actually arose from a bug we encountered. After some investigation, the user attempted to import a “Newline Delimited” JSON document. I’m not familiar with this document type, but cursory research reveals the following:

    There is currently no standard for transporting instances of JSON text within a stream protocol apart from [Websockets], which is unnecessarily complex for non-browser applications.

    A common use case for NDJSON is delivering multiple instances of JSON text through streaming protocols like TCP or UNIX Pipes. It can also store semi-structured data.

    NDJSON spec Github

    If you’d like to learn more about this specification, I recommend visiting this site. It doesn’t seem affiliated with the project, but it is very informative compared to the official NDJSON GitHub page.

    Are you using NDJSON now? Or did you just learn of its existence? Do you think you’d start using it, or do you have any use for it? Let me know. I’m curious about the potential applications.

    New Walkthroughs (Tours) for Charts and Data Modeler

    Updates to the Database Actions Launchpad. 

    We continue to refine the Launchpad, and these are but two more examples. The Data Modeler and Charts pages have Walkthrough Tours (and documentation too):

    What are your thoughts on the new Launchpad? Do you use the “pin” feature? Is it intuitive? What else could we include or improve? Or is it perfect (it’s perfect, isn’t it? …I knew it!)

    Java

    ORDS requires Oracle Java or Oracle GraalVM Enterprise Edition 11, 17, or 21 to run ORDS 24.2.

    If you intend to do anything GraphQL-related, use GraalVM 17. Instructions for downloading can be found here. Just make sure you set your JAVA_HOME to GraalVM 17 so that when ORDS starts up, it does so with GraalVM 17! At this time, ORDS GraphQL support only works with GraalVM 17.

    That’s it for this release. Download the latest and enjoy!

    Oh, and if we missed your enhancement this time, let me know. We’ll add it to one of our sprints!

    Follow

    And don’t forget to follow, like, subscribe, share, taunt, troll, or stalk me!

  • Random Access Memories: ORDS and JWTs

    Random Access Memories: ORDS and JWTs

    Why is this in the OAuth chapter?

    Apparently, JWTs fall under the purview of the OAuth Working Group, a section of the Internet Engineering Task Force (IETF). Here is a draft of the JWT specification I found. This makes sense; I’ve since relearned that OAuth = Open Authorization 🤦🏻.

    ORDS JWT OAUTH parameters

    You’ll notice two new procedures in that package: OAUTH.CREATE_JWT_PROFILE and OAUTH.DELETE_JWT_PROFILE. After getting acquainted with them, I wanted to highlight three parameters of the OAUTH.CREATE_JWT_PROFILE procedure: 

    • p_issuer
    • p_audience
    • p_jwk_url

    Your JWT issuer (e.g., Microsoft Entra or Oracle Identity Cloud Service) will provide you with these three values required for the OAUTH.CREATE_JWT_PROFILE procedure. 

    However, they might be referred to by slightly different names. I first noticed this as I set up Microsoft Entra to work with ORDS (below are images taken from a slide deck I’m working on). 

    Learn more about these parameters.

    So, the names are all slightly different. But if I can figure it out, you definitely can.

    Decoding JWTs

    Once you acquire your JWT, you’ll need a way to decode it so you can use it for testing or development, and you’ll need some of the information for the ORDS profile procedure! Several resources exist, but here is a [non-exhaustive] list I put together: 

    If you choose to use a web-based decoder, it’ll look like this (paste your Token):

    But you might prefer something other than putting your JWT into a browser for decoding. 

    Homegrown

    So, if you’re like me (and didn’t do your research beforehand), you might try to come up with something independently. Something you can run locally.

    I created a JavaScript function that expects a JWT and “splits” on the periods. From there, Base64 decodes the necessary stuff for you and returns it: 

    function decodeJwt(newjwt) {
        var headerJwt = newjwt.split(".")[0];
        var headerJwtdecoded = atob(headerJwt);
        
        var payloadJwt = newjwt.split(".")[1];
        var payloadJwtdecoded = atob(payloadJwt);
    
        var signatureJwt = newjwt.split(".")[2];
        // var signatureJwtdecoded = atob(signatureJwt);
    
        // var signatureJwtBase64 = signatureJwt.replace(/-/g, "+").replace(/_/g, "/");
        // var signatureJwtBase64decoded = atob(signatureJwtBase64);
    
        console.log(headerJwt, payloadJwt, signatureJwt);
    
        console.log(headerJwtdecoded, payloadJwtdecoded);
    
        return(headerJwt, payloadJwt);
      };
    
    
    decodeJwt("Your JWT goes here.");
    

    To illustrate how this function works, I took some “boilerplate” HTML from the Bootstrap docs page and spun up a LiveServer in VS Code. I’m also “inspecting” the results from the console.log();. I’m not really sure how far I’ll take this, especially now that I’ve learned about all the existing libraries. But feel free to remix this code!

    Thank you for your time 🙇🏻‍♂️

    And that’s all I have to share for today! 

    If you still need to download and upgrade to the latest ORDS, you can find the.zip file here. Be sure to explore GraalVM, too; you can “unlock” some newer ORDS features by using GraalVM as your primary Java Developer Kit (JDK)!

    Follow

    And don’t forget to follow, like, subscribe, share, taunt, troll, or stalk me!

  • ORDS, JavaScript, the Fetch API, and HTML

    ORDS, JavaScript, the Fetch API, and HTML

    I found JavaScript and HTML code here and here and “remixed” it to work with one of my sample ORDS APIs. Here is the result:

    the-html-in-browser, chris hoina, db tools, ords, oracle database, javascript, html, ords api, oracle rest api
    ORDS + JavaScript + Fetch API + HTML

    Impressive, no? Care to try it out? Read on friend!

    References

    I’ll front load with all the necessary stuff. That way, you can bounce if you don’t feel like reading. You’ll get the gist if you follow along with what I’ve provided.

    Much of what I learned came from the MDN Web Docs site. I would get acquainted with the following pieces of code (or at least have them handy) since they heavily influenced me (a.k.a. plagiarized).

    MDN Web Docs

    I either used or referenced these files in my version of the code. They are all available in the two links I mentioned above, but I’m adding them here for convenience (in case you need to leave or want to review while on this page).

    ORDS code

    Here are a few things to point out:

    1. In line 16 of my index.html code, I referenced the JavaScript code (script.js) separately. This approach achieves the same effect as embedding the JavaScript directly into the HTML file (as seen in the MDN’s version of the index.html file).
    2. The script.js contains the Fetch API and the JavaScript concept of “promises.” The following were super helpful for me. Maybe the will be for you too:
    3. The JSON file contains an example of what an ORDS GET request response looks like (if viewing in the browser). The structure is nearly identical if you compare it to the MDN JSON file.
      • This means you can take their HTML and JavaScript code and populate it with an ORDS endpoint and [subsequent] response data (i.e., the stuff you see in this localhost.json file).

    Live Server

    I’m also using the Live Server extension for VS Code. If you don’t have it, you’ll need it to run the code I’ve provided. You can download it from the VS Code Marketplace here.

    mentioning-live-server-for-this-exercise, chris hoina, db tools, ords, oracle database, javascript, html, ords api, oracle rest api
    You’ll want Live Server for this one!

    How I met your Mothra 👾

    Where to start? From the beginning, right? What you see below are two JSON files. On the left, from ORDS. On the right, from the MDN Web Docs sample code (direct link to that file).

    Comparing JSÒN

    comparing-ords-and-mdn-json-files, chris hoina, db tools, ords, oracle database, javascript, html, ords api, oracle rest api
    ORDS on the left, MDN on the right.

    They are nearly identical. They are both a JSON object {} comprised of key: value pairs, where the first key’s value is an array []. In both files, this array has more objects {}. And each of those objects has its own key: value pairs…marone 🤌🏼!

    I mention all this because this makes the existing code easy to work with. Which you’ll see shortly.

    Comparing JavaScript

    Next is the JavaScript code; I’ll compare both my version and the MDN Web Docs version.

    comparing-javascript-versions-with-ords-apis, chris hoina, db tools, ords, oracle database, javascript, html, ords api, oracle rest api
    ORDS on the left; can you spot the differences?

    You’ll notice that a lot of the code is quite similar. I kept it this way, so I wouldn’t unintentionally break anything. The main differences in my code are the:

    1. const ordsAPI on Line 1 (as opposed to referencing a JSON file).
    2. Naming conventions in lines 14-27.
    3. listItem.append(); on line 29 is heavily remixed (I did this so I could create individual lines for each entry).
    4. Templating in my code (i.e., wherever you see the little ``` marks; they allow you to embed text directly into the HTML) I use A LOT more of it!

    About the ORDS JSON Object

    If you were to navigate to your ORDS endpoint, it would look like the images below. I’m including them for a couple of reasons:

    1. You can see those key: value pairs in a different presentation.
    2. These images help connect what is coming through in that GET request and what you see in the JavaScript code.
    an-example-of-ords-response-in-browser, chris hoina, db tools, ords, oracle database, javascript, html, ords api, oracle rest api
    The items key with its value (an array).
    the-raw-ords-response-in-browser, chris hoina, db tools, ords, oracle database, javascript, html, ords api, oracle rest api
    Remember the other key: value pairs, too!

    Reviewing the HTML

    Assuming you’ve started up Live Server (along with setting up your environment to mimic my own), you’ll immediately see this beauty of a web page. This image alone doesn’t tell a complete story, though.

    the-html-in-browser, chris hoina, db tools, ords, oracle database, javascript, html, ords api, oracle rest api
    Review line 29 in the JavaScript code; it’ll help to “connect the dots.”

    However, when you open up the developer tools in your browser, you’ll see what is happening under the covers.

    1. Live Server starts up, sees the index.html file, and “serves” it up.
    2. In that HTML file is a reference to script.js; the JavaScript is run.
    3. The JavaScript composes a list and then appends all the data you see here (on screen):
    html-with-developer-tools-to-inspect-html-and-javascript, chris hoina, db tools, ords, oracle database, javascript, html, ords api, oracle rest api
    With developer tools open, you can see the HTML. This HTML should look similar to lines 12-27 of the JavaScript code.

    Summary

    After writing this up, I’m realizing this clearly needs to be a video. But if you get it, great! Otherwise, stay tuned!

    There isn’t anything ground-breaking here. I’m highlighting an example of manipulating existing ORDS JSON objects (with the Fetch API) because I hadn’t seen anything quite like what I am presenting here.

    Also, the web page that I’m showing is very, very basic. I’m neither a UX nor UI designer, so this is what you get, folks!

    The main point is that the ORDS APIs are effortless to work with if you have a fundamental understanding of manipulating JSON objects using JavaScript. They are no different than what you see out in the wild.

    Some follow-up

    I want to take this and add some React to it. And I’d also like to add authentication (Basic, OAuth 2.0, and Java Web Tokens). But baby steps.

    Okay, that’s all for now, folks, Sayonara!

    Follow

    And don’t forget to follow, like, subscribe, share, taunt, troll, or stalk me!