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:

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:
|
POST: / | Adds a topic | The topic object in the request body
|
Empty |
GET: /TOPICID | Gets the detail of a topic by TOPICID | Empty | Returns a JSON object with the detail of the
topic:
|
POST: /TOPICID | Adds a comment to the topic by TOPICID | The comment and topic ID in a JSON object:
|
Empty |
The client application has the following flow:

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?
- Node.js 4.x
- A text editor
- A web browser
- To deploy the application to Oracle Application Container Cloud you need an active account.
- RESTful-project.zip (This is the RESTful project with source code. Download it to view the completed project.)
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.
-
Create a new file and name it
server.js
. Openserver.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.
-
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 seeHELLO WORLD
enter the URL:http://localhost:8089/demo
, where you should again seeHELLO WORLD
.In the console window press Ctrl+C to exit the server.
Note: Any URL that begins withhttp://localhost:8089
will have the same result. -
After the port declaration, add the following variables to store the topics of the message board:
var topicList = [];
var topicDetail = {};
var currentId = 123; -
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;
} -
Add a function to add comments:
function addComment(topicId, text) {
console.log("addComment(" + topicId + "," + text + ")");
topicDetail[topicId].comments.push(text);
} -
(Optional) Add some elements to the
topicList
andtopicDetail
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"); -
Locate and remove the
response.end("HELLO WORLD");
line inside thecreateServer
function argument. You'll modify this function in the following steps. -
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. -
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
}); -
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 functionhandleRequest(request, response, requestBody)
inside therequest.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);
}); -
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:
var http = require('http');
var PORT = 8089;
var topicList = [];
var topicDetail = {};
var currentId = 123;
function addTopic(tTitle, tText) {
console.log("addTopic(" + tTitle + "," + tText + ")");
var topicId = ++currentId;
topicList.push({title: tTitle, id: topicId});
topicDetail[topicId] = {title: tTitle, text: tText, comments: []};
return topicId;
}
function addComment(topicId, text) {
console.log("addComment(" + topicId + "," + text + ")");
topicDetail[topicId].comments.push(text);
}
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");
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);
console.log('TopicList=' + JSON.stringify(topicList));
console.log('TopicDetail=' + JSON.stringify(topicDetail));
var requestBody = '';
request.on('data', function (data) {
requestBody += data;
});
request.on('end', function () {
handleRequest(request, response, requestBody);
});
});
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]));
}
}
}
server.listen(PORT, function () {
console.log('Server running...');
});
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.
-
Create a new empty
index.html
file. -
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> -
Add the HTML layout to the body tag, using
div
elements and ID attributes to define each section.Add these sections and elements:
loading
, Thisdiv
contains a message displayed while the page loads:<div id="loading">
<h2>Please wait...</h2>
</div>topic_list
, Thisdiv
contains the topic list:<div id="topic_list" style="display: none;">
</div>new_topic
, Thisdiv
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
, Thisdiv
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
, Thisdiv
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> -
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";
- 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 alldiv
elements and show theloading
element.//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', '');
} -
Add a function
loadAllTopics()
. First, call thehideAll
function to display theloading
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 theJSON.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
andnew_topic
elements visible and hide theloading
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();
} -
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
andcomment
elements and hide theloading
element.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();
} -
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 thevalue
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 withJSON.stringify(obj)
, whereobj
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));
} -
Create a new
addNewComment()
function. Inside the function, create an object with thenewComment
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));
} -
Finally, to initialize the page, add a function handler to the
window.onload
event and call thehideAll()
function followed by theloadAllTopics()
function:window.onload = function () {
hideAll();
loadAllTopics();
}
The completed index.html
file should
look like this:
<!DOCTYPE html>
<html>
<head>
<title>MessageBoard</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>
var url = "http://localhost:8089";
//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', '');
}
// 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();
}
//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();
}
//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));
}
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));
}
window.onload = function () {
hideAll();
loadAllTopics();
}</script>
</head>
<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>
</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.
-
Open a new console window and go to the folder where you saved the
server.js
file. -
Start the Node.js server:
node server.js
The server will start running. Keep the console window open.
View ImageDescription of this image -
In a web browser open the
index.html
file.View ImageDescription of this image -
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. -
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 amanifest.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.
-
Open the
server.js
file in a text editor -
Locate the
PORT
variable declaration and change it to a JavaScript conditional statement so that if theprocess.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.
-
Create a new
manifest.json
file. -
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. -
Compress both the
server.js
andmanifest.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.
In a web browser, go to https://cloud.oracle.com/home and click Sign In.
From the Cloud Account menu, select Traditional Cloud Account, select your data center, and click My Services.
Enter your identity domain and click Go.
Enter your cloud account credentials and click Sign In.
- In the Oracle Cloud My Services dashboard, click Action
, click Services, and select Application Container.
-
In the Applications page, click Create Application, and select Node.
-
Enter a name for your application, select Upload Archive, and click Browse.
View ImageDescription of this image -
In the File Upload dialog box, select the file that you created in the previous section, and click Open.
-
In the Create Application dialog box, enter any details for the application and click Create.
View ImageDescription of this image -
Wait until the application is deployed, and then click the URL.
-
Copy the URL of your deployed application from the Oracle Application Container Cloud Service home page. You'll use it in the next section.
View ImageDescription 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.
-
Open the
index.html
file in a text editor. -
Locate the line where you define the server URL:
var url = "http://localhost:8089";
-
Replace the
"http://localhost:8089"
with the URL of your deployed application that you copied in the previous section of this tutorial.View ImageDescription of this image -
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.