Recommended Reading: Chapters 11 and 18 in the JavaScript book

Full Cafe Example

JavaScript Code

Interacting with a REST server

HTTP is the protocol that browsers use to communicate with web servers. When Tim Berners-Lee developed HTTP he envisioned something a little more complex than a system for just fetching web pages. In his original vision for the web, he imagined a system in which users could fetch web pages, edit them in a browser, and upload their edits back to a web server. To facilitate this, he provided HTTP with four different verbs.

VerbWhat it does
GETFetches data (usually a web page)
POSTUploads data to the server
PUTUpdates data on the server
DELETERemoves data from the server

In addition to accomodating a range of data operations, HTTP also offers standardized ways to transfer data. For example, when a browser sends a GET command to a server, it knows to expect data in the response. Specifically, when the response comes back it will have a body, and the requested data will appear in that body. Likewise, a POST command going to a server can have a body, and that body can contain data that we want to upload to the server.

The upshot of all of this is that HTTP was built right from the very start as a system for managing the flow of data back and forth between browsers and servers. Most of these capabilities stayed dormant for many years and were not used. Eventually, though, someone figured out how to leverage the full power of HTTP to make a data architecture based on the web and HTTP. That architecture is called REST, and it uses HTTP (verbs and URLs) along with a data representation (XML or JSON) to make a complete computing infrastructure. REST is another key element of the modern web style of programming, in that modern web applications are designed to interact with REST based servers that offer what are known as RESTful web services.

Working with a RESTful web service

To begin to explain how REST web services work, I have set up a simple web service on our class web server machine. This web service serves up data on menu items and orders at an imaginary cafe. In today's main example we will be using the web service to download a list of menu items to a web page, and to allow us to place orders and upload them to the server.

To send a request to download the menu items we send a GET request to the URL:

https://cmsc106.net/cafe/item

In return, the web service will respond by serving up some data. In this case, the web service will respond with

[{"iditem":1,"name":"Breakfast Burrito","cost":495,"meal":"breakfast"},...,{"iditem":10,"name":"Stir Fry","cost":895,"meal":"dinner"}]

This is a JSON encoded list of menu items available at the cafe.

Sending a query to a web service from JavaScript

Since we are going to be using a web service as a data source, we next need to cover how to contact the web service from a JavaScript program. modern JavaScript provides the fetch API to make it easy to do REST requests.

The sample code below demonstrates how a fetch() get request works:

fetch('https://cmsc106.net/cafe/item')
.then(response => response.json())
.then(response => receiveItems(response));

The fetch() method takes a URL as its parameter and initiates the request. fetch() returns a special object called a promise. We then call the then() method on that promise object to hand it some code to run when the promise has a result. We call then() twice here: the first call takes the text of the response from the server (which we know in advance will be JSON code) and turns it into an array of objects. The second call passes the list of objects we receive from the server to a function that processes the data.

Once the user has selected some items to order and has entered their name and phone number in the order form we will want to POST their order to the server. The code below shows how to do a POST using the fetch() API:

let toPost = {customer:nameField.value,phone:phoneField.value,items:ids};
// POST it to the server
fetch('https://cmsc106.net/cafe/purchase', {
  method: 'POST',
  body: JSON.stringify(toPost),
  headers: {
    'Content-type': 'application/json; charset=UTF-8'
  }
}).then(response => response.json()).then(response => showConfirm(response));

Other special features in the example

Today's example program also illustrates a number of new DOM manipulation techniques. The first special technique involves the structure of the page. If you take a look at the HTML for the page you will see that the body of the page has the following overall structure:

<body>
  <div id="starting">
    <p>Loading data...</p>
  </div>

  <div id="view">
    ... HTML to display the menu
  </div>

  <div id="order">
  ... HTML to display the order details
  </div>

  <div id="confirm">
    ... HTML to display confirmation message
  </div>
</body>

To the user, the application looks like several different pages: a page to display the menu, a page to display the order, and a page that shows a confirmation message when the order has been placed. In fact, the HTML code for this application consists of a single page with four distinct divs in it. As the application runs we will be showing and hiding this these divs to create the illusion that the user is moving through a series of distinct pages.

To show and hide the divs we use code like the following. In the setUp() function for the application we do

let menuDiv = document.getElementById('view');
menuDiv.style.display = 'none';
let orderDiv = document.getElementById('order');
orderDiv.style.display = 'none';
let confirmDiv = document.getElementById('confirm');
confirmDiv.style.display = 'none';

This hides every div but the first one. Once we have successfully received the menu data from the server we then do

let startingDiv = document.getElementById('starting');
startingDiv.style.display = 'none';

let menuDiv = document.getElementById('view');
menuDiv.style.display = 'block';

This hides the div that shows the loading message and shows the div that displays the menu.

The next special feature involves some of the buttons we put in tables in the application. For example, in the table that displays the menu items we will want to add an order button to each row so the user can order the items in the menu.

Here is the code that builds the menu table:

function displayItems(items) {
  // Remove any existing entries from the table
  let tableBody = document.getElementById('menu');
  tableBody.innerHTML = '';

  // Add new rows for the items in the list
  let length = items.length;
  for(let n = 0;n < length;n++) {
    let newRow = document.createElement('tr');
    newRow.innerHTML = '<td class="align-middle">'+items[n].name+'</td><td class="align-middle">$'+items[n].cost/100+'</td>';
    let orderButton = document.createElement('button');
    orderButton.innerText = 'Order';
    orderButton.addEventListener('click',()=>orderItem(items[n].iditem));
    orderButton.setAttribute('class','btn btn-secondary align-middle');
    let buttonTD = document.createElement('td');
    buttonTD.appendChild(orderButton);
    newRow.appendChild(buttonTD);
    tableBody.appendChild(newRow);
  }
}

A lot of the logic here is repeated from the first Cafe example I showed. We are using DOM techniques here to build tr elements to add to a table. A new feature is that we want each table row to also include a button that the user can click to order that item. As you can see here, the JavaScript code creates a button element, sets its text and class, and then also attaches an event handler to it. We then also construct a td element, put the button in the td, and then finally append the td as a child of the row.