Linking Application Container Cloud to DBaaS - Expose REST API from node.js application leveraging node-oracle-database driver image 119

Linking Application Container Cloud to DBaaS – Expose REST API from node.js application leveraging node-oracle-database driver

In a recent article, I discussed the deployment of a simple Node.js application to the Oracle Application Container Cloud. I take this one step further in this article: I extend the node.js application to connect to a DBaaS instance using the Oracle node.js Database Driver and expose a REST API to retrieve data (departments, if you want to know).

This figure illustrates what this article describes:

image

The steps we go through in this article are:

  • Create the Node.js application with node-oracledb based database interaction
  • Deploy the Node.js application to Application Container Cloud
  • Configure the application on Application Container Cloud: service binding and custom environment variables
  • Access the REST API exposed by the Node.js application
  • The node-oracledb driver connects node.js applications to an Oracle Database. It is an open source project with Apache 2.0 license. It is maintained by Oracle and is under active development. Sources can be found on GitHub.The node-oracledb driver is available out of the box in a Node.js Application Container Cloud instance. No special steps are required to have access to this driver from a node.js application that is deployed to the Application Container Cloud instance. All you need to do is include the oracledb module in the node.js application (using var oracledb = require(‘oracledb’);) and of course interacting with the database in the regular way supported by node-oracledb.

    Create the Node.js application with node-oracledb based database interaction

    The application is a very simple one. It handles requests – and responds only in anger to a request that goes like this: http://host:port/departments. If that is the url – then the relative url is just /departments and that is what the code responds to in function handleRequest(). When you check the code in dataApi.js, you will find a few things of notice:

    • var oracledb = require(‘oracledb’); — this is the line that brings in the node-oracledb Database Driver. This module does not have to be declared in the package.json meta-file nor does it have to be included in the application archive: it is available out of the box in the Node.js application container on Oracle Application Container Cloud
    • var connectString = process.env.DBAAS_DEFAULT_CONNECT_DESCRIPTOR.replace(“PDB1”, “demos”); — this is the line that first of all leverages the environment variable DBAAS_DEFAULT_CONNECT_DESCRIPTOR that is automatically set up when the Service Binding is created to the DBaaS instance (in the next section). This default connect descriptor refers to the PDB1 pluggable database in my DBaaS instance; however, the HR schema with the DEPARTMENTS table are in the demoes pluggable database, hence the replace action.
    • oracledb.getConnection(… — this line acquires the connection to an Oracle database, using two custom environment variables – DB_USER and DB_PASSWORD. Note: any serious application should be using a connection pool (supported too in the node-oracledb database driver) instead of a such a fairly expensive one-off connection
    • connection.execute(   selectStatement   … — this line (and subsequent lines) fire off the select statement to the database, in this case to gather department records.
    • response.end(JSON.stringify(result.rows)); — this line takes the result set from the database call and turns them in a JSON string representation that is written to the response
    • connection.release(… — the database connection is returned; again: a real application would use connections from a pool, that would be returned in a similar manner

    The code handles various error situations – failed to get a connection, failed to execute the SQL statement, failed to release connection – and returns HTTP responses with error details. Suitable debug-statements (and oh boy, how I needed them) are written to the console and therefore to the log files of Application Container Cloud.

    var http = require('http');
    var oracledb = require('oracledb');
    
    var PORT = process.env.PORT || 8089;
    
    function handleRequest(request, response, requestBody) {
    console.log(request.method + ":" + request.url + ' >>' + requestBody);
    
    if(request.url === "/departments"){
    console.log('Handle request: '+request.url);
    var connectString = process.env.DBAAS_DEFAULT_CONNECT_DESCRIPTOR.replace("PDB1", "demos");
    console.log('ConnectString :' + connectString);
    oracledb.getConnection(
    {
    user : process.env.DB_USER || "hr",
    password : process.env.DB_PASSWORD || "hr",
    connectString : connectString
    },
    function(err, connection)
    {
    if (err) {
    console.log('Error in acquiring connection ...');
    console.log('Error message '+err.message);
    
    // Error connecting to DB
    response.writeHead(500, {'Content-Type': 'application/json'});
    response.end(JSON.stringify({
    status: 500,
    message: "Error connecting to DB",
    detailed_message: err.message
    }));
    
    return;
    }
    var selectStatement = "SELECT department_id, department_name " +
    "FROM departments ";
    console.log('Connection acquired ; go execute ');
    
    connection.execute( selectStatement
    , {}, {
    outFormat: oracledb.OBJECT // Return the result as Object
    }, function (err, result) {
    if (err) {
    console.log('Error in execution of select statement'+err.message);
    response.writeHead(500, {'Content-Type': 'application/json'});
    response.end(JSON.stringify({
    status: 500,
    message: "Error getting the departments",
    detailed_message: err.message
    }));
    
    } else {
    console.log('db response is ready '+result.rows);
    response.writeHead(200, {'Content-Type': 'application/json'});
    response.end(JSON.stringify(result.rows));
    }
    // Release the connection
    connection.release(
    function (err) {
    if (err) {
    console.error(err.message);
    } else {
    console.log("GET /departments : Connection released");
    }
    });
    });
    });
    }
    else {
    response.writeHead(200, {'Content-Type': 'text/html'});
    response.write("No Data Requested, so none is returned");
    response.end();
    
    }
    }//handleRequest
    
    var server = http.createServer(function (request, response) {
    response.setHeader('Access-Control-Allow-Origin', '*');
    response.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
    response.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
    response.setHeader('Access-Control-Allow-Credentials', true);
    
    var requestBody = '';
    request.on('data', function (data) {
    requestBody += data;
    });
    request.on('end', function () {
    handleRequest (request, response, requestBody);
    });
    
    });
    
    server.listen(PORT, function () {
    console.log('Server running...');
    });
    

    Deploy the Node.js application to Application Container Cloud

    The application is packaged in a zip-file together with manifest.json and package.json – as described in my previous article.

    image

    Next is deployment, also described in the previous article. Create a new application of type Node.js and upload the application archive. In this case I have cut some corners and uploaded a new version of an existing application (message board) using this dataApi.zip application archive:

     

    Configure the application on Application Container Cloud: service binding and custom environment variables

    Because I want to access a DBaaS instance from the Application Container Cloud, I create a service binding from the latter to the former. On the Deployments tab, I click on the button Add Service Binding:

    image

    A dialog appears that allows me to select the type of PaaS service [instance] to which I want to bind:

    image

    Then I see a list of all instances of that type that I have access to in the same identity domain:

    image

    I provide the credentials for this instance and press Save.

    The result is that a number of environment variables – related to the DBaaS instance – is added to the application:

    SNAGHTML1db7509

    These variables can be accessed from the Node.js application, as we saw earlier in this article, using the process.env.NAME_OF_VARIABLE construction. Note that I have to Apply the Edits which forces a restart of the application.

    However, hold your horses. We also have to add two custom environment variables. The code in dataApi.js contains this section:

    oracledb.getConnection(
    {
    user          : process.env.DB_USER || “hr”,
    password      : process.env.DB_PASSWORD || “hr”,
    connectString : connectString
    },

    The two values process.env.DB_USER and process.env.DB_PASSWORD are provided – injected if you prefer – from outside the application container in the form of environment variables.

    Therefore, we need to add two environment variables. Click on Add Environment Variable

    SNAGHTML4802707

    Enter Name and Value and press Save.

    image

    Press Add Environment Variable one more time. Enter the new Name and Value

    SNAGHTML4835142

    and press Save again.

    SNAGHTML483e0f3

    The two new custom environment variables have been defined. After applying these changes – causing a restart of the application – these values are accessible from within the container and the application.
    image

    At this time, restarting the application means reprovisioning the container and redeploying the application.

    image

    This should be quite fast – after all, launching a Docker Container from a local image can be very fast. Unfortunately, my experience so far is that it takes pretty long (over five minutes). I assume this is a startup issue, not a permanent affliction.

     

     

     

     

    Access the REST API exposed by the Node.js application

    Once the restart is complete, we can access the application.

    Click on the URL shown at the top of the page

    image

    This URL is not one handled in any specific manner, so dataApi.js returns:

    image

    However, when the URL is extended with /departments :

    image

    The data is retrieved by the Node.js application from the DBaaS instance and returned as a JSON message.

    image

    Resources

    Blog Article: Deploying a node.js application to the Oracle Application Container Cloud Service

    Home of Node.js Oracle Database Driver oracle-db

    OTN –  Node.js Developer Center

    Download the Node.js application: dataApi.zip.