Developing a RESTful Node.js and HTML5 application to Oracle Application Container Cloud Service

 

Before You Begin

Purpose

This tutorial shows you how to create and deploy a Node.js RESTful application to Oracle Application Container Cloud Service additionally, you create an HTML5 application to test your REST service locally and in the cloud.

Time to Complete

  • Server Development: 30 minutes
  • Client Development: 30 minutes
  • Cloud Deployment: 40 minutes

Background

Oracle Application Container Cloud Service lets you run Node.js applications in the cloud. Node.js is a lightweight and efficient platform for building and running microservices. Node.js is sometimes referred to simply as Node.

Scenario

For this project, you create a Node.js REST web service and an HTML5 and JavaScript client. These are two separate applications. To keep your files sorted and prevent confusion, you create two different folders and put the files of each application inside:

Application Structure diagram
Description of this image

The completed project, the MessageBoard application, provides a simple forum with topics and comments. Using REST, you provide access to the list of topics in the forum, as well as details of a particular topic. Data is exchanged using JavaScript Object Notation (JSON).

The Node.js REST server application has the following endpoints:

Path Description Request Response
GET: / Gets the list of topics Empty Returns a JSON array of topics objects:
[
{title:"TopicTitle",id:"TopicId"},
{title:"TopicTitle",id:"TopicId"}
]
POST: / Adds a topic The topic object in the request body
{title:"TopicTitle",text:"Topic text"}
Empty
GET: /TOPICID Gets the detail of a topic by TOPICID Empty Returns a JSON object with the detail of the topic:
{
title: "Topic Title",
text: "Topic text",
comments: ["comment1", "comment2"]
}
POST: /TOPICID Adds a comment to the topic by TOPICID The comment and topic ID in a JSON object:
{topicId:TopicId, text:"comment text"}
Empty

The client application has the following flow:

Application flow diagram
Description of this image

Note on ports: The server application uses port 8089 for communication, but you can change it. The standard port for HTTP is 80, but sometimes it's already reserved and your application can't start because of a port conflict. Oracle Application Container Cloud Service provides the port that your application should use.

What Do You Need?

 

Developing the RESTful Node.js Service

This section explains how to create a simple HTTP RESTful web service using Node.js. Note that the code uses only libraries included in Node.js; it doesn't use any third-party REST frameworks or have any NPM dependencies.

  1. Create a new file and name it server.js. Open server.js in a text editor, and add the following code to create a simple HTTP service:

    var http = require('http');

    var PORT = 8089;

    var server = http.createServer(function (request, response) {
    response.end("HELLO WORLD");
    });

    server.listen(PORT, function () {
    console.log('Server running...');
    });

    The preceding code creates an HTTP service that listens on the port 8089.

  2. Test the server by opening it in the console window with the following command:

     node server.js

    Open a browser and go to http://localhost:8089. You should see HELLO WORLD enter the URL: http://localhost:8089/demo, where you should again see HELLO WORLD.

    In the console window press Ctrl+C to exit the server.

    Note: Any URL that begins with http://localhost:8089 will have the same result.
  3. After the port declaration, add the following variables to store the topics of the message board:

    var topicList = [];
    var topicDetail = {};
    var currentId = 123;
  4. Add a function to add topics:

    function addTopic(tTitle, tText) {
    var topicId = ++currentId;
    topicList.push({title: tTitle, id: topicId});
    topicDetail[topicId] = {title: tTitle, text: tText, comments: []};
    return topicId;
    }
  5. Add a function to add comments:

    function addComment(topicId, text) {
    console.log("addComment(" + topicId + "," + text + ")");
    topicDetail[topicId].comments.push(text);
    }
  6. (Optional) Add some elements to the topicList and topicDetail elements to have an initial state:

    var id1 = addTopic("Topic 1","Topic 1 content");
    var id2 = addTopic("Topic 2","Topic 2 content");
    addComment(id1, "Good topic");
    addComment(id2, "This is a comment");
    addComment(id2, "This is another comment");
  7. Locate and remove the response.end("HELLO WORLD"); line inside the createServer function argument. You'll modify this function in the following steps.

  8. Add the following response headers to support calls from any external client:

    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);

    Note:Browsers and applications usually prevent calling REST services from different sources. Thus, if you run the client on Server A and the REST services on Server B, then you must provide a list of known clients in Server B by using the Access-Control headers. Clients check these headers to allow invocation of a service. This is used to prevent cross-site scripting attacks (XSS). You can learn more about XSS in the Open Web Application Security Project page for XSS.

  9. To get the body of the request, use the following code:

    var requestBody = '';
    request.on('data', function (data) {
    requestBody += data;
    });
    request.on('end', function () {
    //request body ready inside the requestBody variable
    });
  10. To create the RESTful service structure, you must filter the request by HTTP method and URL. To do this, you create a new function.
    Invoke the function handleRequest(request, response, requestBody) inside the request.on('end') handler.
    If you copied the code in the previous step, then replace the comment with the new function, so the code looks like the following:

    request.on('end', function () {
    handleRequest (request, response, requestBody);
    });
  11. Create the handle request function:

    function handleRequest(request, response, requestBody) {
    console.log(request.method + ":" + request.url + ' >>' + requestBody);
    if (request.url == '/') {
    if (request.method == 'POST') {
    var jsonMsg = JSON.parse(requestBody);
    addTopic(jsonMsg.title, jsonMsg.text);
    response.end();
    } else {
    response.end(JSON.stringify(topicList));
    }
    } else {
    var topicId = request.url.substring(1);
    if (request.method == 'POST') {
    var jsonMsg = JSON.parse(requestBody);
    addComment(jsonMsg.topicId, jsonMsg.text);
    response.end();
    } else {
    response.end(JSON.stringify(topicDetail[topicId]));
    }
    }
    }

The completed server.js file should look like this:

View server.js

Note: Some console.log outputs were added to the file to help with debugging.

 

Developing the HTML5 Client

The HTML5 and JavaScript client is a simple HTML file that you create and store separately from the server application on your machine. You don't need to keep the server and the client applications in the same folder, because they run on separate machines. The server runs in the cloud and the client runs on your PC.

You can create a CSS stylesheet or separate the files after you finish and test the file. This example doesn't have any associated styles or scripts.

  1. Create a new empty index.html file.

  2. Open the file in a text editor and add the following code to create a basic HTML5 page:

    <!DOCTYPE html>
    <html>
    <head>
    <title>MessageBoard</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>
    </script>
    </head>
    <body>
    </body>
    </html>
  3. Add the HTML layout to the body tag, using div elements and ID attributes to define each section.

    Add these sections and elements:

    • loading, This div contains a message displayed while the page loads:
      <div id="loading">
      <h2>Please wait...</h2>
      </div>
    • topic_list, This div contains the topic list:
      <div id="topic_list" style="display: none;">
      </div>
    • new_topic, This div contains the form for a new topic:
      <div id="new_topic" style="display: none;">
      <h3>New Topic</h3>
      Title: <input type="text" id="new_topic_title" value=""><br>
      Content: <input type="text" id="new_topic_text" value=""><br>
      <input type="button" value="Add new topic" onclick="addNewTopic();">
      </div>
    • topic_detail, This div contains the detail of the selected topic: loads
      <div id="topic_detail" style="display: none;">
      <input type="button" value="Back" onclick="loadAllTopics()">
      <h4 id="topic_detail_title"></h4>
      <div id="topic_detail_text"></div>
      <div id="topic_detail_comments"></div>
      </div>
    • comment, This div contains a form to add a comment to the selected topic:
      <div id="comment" style="display: none;">
      <h3>Add new Comment</h3>
      <input type="hidden" id="comment_topic_id" value="0">
      <input type="text" id="comment_text" value="">
      <input type="button" value="Add new comment" onclick="addNewComment();">
      </div>

    The entire body tag and its content should look like this:

    <body>
    <div id="loading">
    <h2>Please wait...</h2>
    </div>
    <div id="topic_list" style="display: none;">
    </div>
    <div id="new_topic" style="display: none;">
    <h3>New Topic</h3>
    Title: <input type="text" id="new_topic_title" value=""><br>
    Content: <input type="text" id="new_topic_text" value=""><br>
    <input type="button" value="Add new topic" onclick="addNewTopic();">
    </div>
    <div id="topic_detail" style="display: none;">
    <h4 id="topic_detail_title"></h4>
    <div id="topic_detail_text"></div>
    <div id="topic_detail_comments"></div>
    </div>
    <div id="comment" style="display: none;">
    <h3>Add new Comment</h3>
    <input type="hidden" id="comment_topic_id" value="0">
    <input type="text" id="comment_text" value="">
    <input type="button" value="Add new comment" onclick="addNewComment();">
    </div>
    </body>
  4. Add JavaScript code to the body of the script tag in the head of the HTML document to provide the required functionality. Start by adding a variable to store the service location. To test locally, use http://localhost:8089. When the server is deployed in the cloud, you'll change this variable.

    var url = "http://localhost:8089";
  5. Add a function to hide all the elements in the page. You call this function each time the user clicks an item. This function sets or removes the style attribute to hide or display an item. In this function hide all div elements and show the loadingelement.
    //Hide all elements; display loading screen
    function hideAll() {
    document.getElementById('topic_list').setAttribute('style', 'display:none');
    document.getElementById('new_topic').setAttribute('style', 'display:none');
    document.getElementById('topic_detail').setAttribute('style', 'display:none');
    document.getElementById('comment').setAttribute('style', 'display:none');
    document.getElementById('loading').setAttribute('style', '');
    }
  6. Add a function loadAllTopics(). First, call the hideAll function to display the loading element. Next, create an AJAX GET request to the / resource:

    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", url + "/", true);
    xmlhttp.onreadystatechange = function () {
    if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
    //Handle response
    }
    };
    xmlhttp.send();

    Handle the response by parsing the content of the xmlhttp.responseText variable with the JSON.parse function.

    Create the list of topics by creating an unsorted HTML list with links inside that invoke the displayTopic() function when clicked.

    Remember to make the topic_list and new_topic elements visible and hide the loading element.

    The completed loadAllTopics() function looks like this:

    // Load all the topics
    function loadAllTopics() {
    hideAll();
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", url + "/", true);
    xmlhttp.onreadystatechange = function () {
    if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
    var topics = JSON.parse(xmlhttp.responseText);
    var tStr = '<h1>Topics</h1>';
    tStr += '<ul>';
    for (i = 0; i < topics.length; i++) {
    tStr += '<li><a href="#" onclick="displayTopic(' + topics[i].id + ')">' + topics[i].title + '</a></li>';
    }
    tStr += '</ul>';
    document.getElementById('topic_list').innerHTML = tStr;
    document.getElementById('loading').setAttribute('style', 'display:none');
    document.getElementById('new_topic').setAttribute('style', '');
    document.getElementById('topic_list').setAttribute('style', '');
    }
    };
    xmlhttp.send();
    }
  7. To display a topic when the user clicks a link, create a displayTopic(topicId) function.

    As with the previous function, first call the hideAll() function and create an AJAX GET request; this time, request the /topicId resource.

    Parse the response as you did with the previous function and use the data to populate the elements inside the topic_detail element.

    Display the topic_detail and comment elements and hide the loadingelement.

    The completed displayTopics(topicID) function looks like this:

    //If the user clicks a topic, then display the topic on screen
    function displayTopic(topicId) {
    hideAll();
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", url + "/" + topicId, true);
    xmlhttp.onreadystatechange = function () {
    if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
    var topic = JSON.parse(xmlhttp.responseText);

    document.getElementById("comment_topic_id").value = topicId;
    document.getElementById("topic_detail_title").innerHTML = topic.title;
    document.getElementById("topic_detail_text").innerHTML = topic.text;
    var comments = "<ul>";
    for (i = 0; i < topic.comments.length; i++) {
    comments += "<li>" + topic.comments[i] + "</li>";
    }
    comments += "</ul>";
    document.getElementById("topic_detail_comments").innerHTML = comments;

    document.getElementById('loading').setAttribute('style', 'display:none');
    document.getElementById('topic_detail').setAttribute('style', '');
    document.getElementById('comment').setAttribute('style', '');
    }
    };
    xmlhttp.send();
    }
  8. Create a new addNewTopic() function. You'll use this function to send an AJAX POST request to the / resource.

    Before creating the AJAX request, you must create the object that you'll send to the server. Start by creating a new JavaScript object with {}. Then set its title and text properties from the value attribute of the form elements in the page.

    In the response handler of the AJAX request, call the loadAllTopics() function.

    Finally, in the send method of the AJAX request, add a parameter with JSON.stringify(obj), where obj is the object that you created with the topic data.

    //If adding a new topic
    function addNewTopic() {
    hideAll();
    var newTopic = {};
    newTopic.title = document.getElementById('new_topic_title').value;
    newTopic.text = document.getElementById('new_topic_text').value;
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("POST", url + "/", true);
    xmlhttp.onreadystatechange = function () {
    if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
    loadAllTopics();
    }
    };
    xmlhttp.send(JSON.stringify(newTopic));
    }
  9. Create a new addNewComment() function. Inside the function, create an object with the newComment data and send it using an AJAX POST request to the /topicId resource.

    The code is very similar to the previous function, except that you call the displayTopic(topicId) function in the response handler.

    function addNewComment() {
    hideAll();
    var newComment = {};
    newComment.topicId = document.getElementById('comment_topic_id').value;
    newComment.text = document.getElementById('comment_text').value;
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("POST", url + "/" + newComment.topicId, true);
    xmlhttp.onreadystatechange = function () {
    if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
    displayTopic(newComment.topicId);
    }
    };
    xmlhttp.send(JSON.stringify(newComment));
    }
  10. Finally, to initialize the page, add a function handler to the window.onload event and call the hideAll() function followed by the loadAllTopics() function:

    window.onload = function () {
    hideAll();
    loadAllTopics();
    }

The completed index.html file should look like this:

View index.html
 

Testing the Completed Project

To test the server and client applications locally, you run the Node.js server and then open the index.html page in a browser.

  1. Open a new console window and go to the folder where you saved the server.js file.

  2. Start the Node.js server:

    node server.js

    The server will start running. Keep the console window open.

    Console Window
    Description of this image
  3. In a web browser open the index.html file.

    Application running in a Web Browser
    Description of this image
  4. Create topics and comments.
    Fill and submit the add topic form to add a topic.
    Click a topic, fill and submit the add comment form to add a comment.
    Go to the page by clicking the topics. Verify that your topics and comments are added.

  5. To stop the server, go to the console window where the server.js is running and press Ctrl+C.

 

Preparing the Node.js Server Application for Cloud Deployment

For your server application to run properly on Oracle Application Container Cloud Service, it must comply with the following requirements:

  • The application must be bundled in a .zip file.
  • In addition to the server.js file, the .zip file must contain a manifest.json file that specifies what command Oracle Application Container Cloud Service should run.
  • Your application must listen to requests on a port provided by the PORT environment variable. Oracle Application Container Cloud uses this port to redirect requests made to your application.
  1. Open the server.js file in a text editor

  2. Locate the PORT variable declaration and change it to a JavaScript conditional statement so that if the process.env.PORT variable is set, then that value is used; otherwise, a default port is used:

    var PORT = process.env.PORT || 80;

    With this declaration, your application listens to the port that Oracle Application Container Cloud provides. Note that this port isn't necessarily the port where your service will be located; the final address of your service is provided by the Oracle Application Container Cloud administration console.

  3. Create a new manifest.json file.

  4. Open the manifest.json file in a text editor and add the following content:

    {
    "runtime":{
    "majorVersion":"4"
    },
    "command": "node server.js",
    "release": {},
    "notes": ""
    }

    The manifest.json file contains the target platform and the command to be run.

  5. Compress both the server.js and manifest.json files together in a .zip file.

You'll use this .zip file to deploy to Oracle Application Container Cloud Service.

 

Deploying the Application to Oracle Application Container Cloud Service

To deploy the application to Oracle Application Container Cloud Service you use the .zip file that you created in the previous section. You use the administration console to upload and deploy the application.

  1. In a web browser, go to https://cloud.oracle.com/home and click Sign In.

  2. From the Cloud Account menu, select Traditional Cloud Account, select your data center, and click My Services.

  3. Enter your identity domain and click Go.

  4. Enter your cloud account credentials and click Sign In.

  5. In the Oracle Cloud My Services dashboard, click Action Action menu icon, click Services, and select Application Container.
  6. In the Applications page, click Create Application, and select Node.

  7. Enter a name for your application, select Upload Archive, and click Browse.

    Oracle Application Container Cloud Service Create Application dialog box
    Description of this image
  8. In the File Upload dialog box, select the file that you created in the previous section, and click Open.

  9. In the Create Application dialog box, enter any details for the application and click Create.

    Oracle Application Container Cloud Service Create Application dialog box
    Description of this image
  10. Wait until the application is deployed, and then click the URL.

  11. Copy the URL of your deployed application from the Oracle Application Container Cloud Service home page. You'll use it in the next section.

    Oracle Application Container Cloud Service Home
    Description of this image
 

Connecting to the Service with the HTML5 and JavaScript Client

Now that the application is running in the cloud, modify the client application to connect to the cloud server.

  1. Open the index.html file in a text editor.

  2. Locate the line where you define the server URL: var url = "http://localhost:8089";

  3. Replace the "http://localhost:8089" with the URL of your deployed application that you copied in the previous section of this tutorial.

    Browser windows and text editor displaying URL to be replaced
    Description of this image
  4. Open the index.html file in a web browser and test the application.

Your client HTML5 application is now connected to the Node.js service that you deployed to Oracle Application Container Cloud Service.

 

Want to Learn More?