Javascript Web Page using an API
A discussion on the frontend usage of an API. This is about creating a page that is well organized and updates based on the backend code created for the api.
The code below holds the info that is being generated into an HTML table.
Key things to know:
- < table > creates a TABLE
- < tr > creates a ROW
- < th > makes the text a column HEADER
- < tbody id = "results" > defines an element id, to be used within JavaScript
<!-- HTML table fragment for page -->
<table>
<thead>
<tr>
<th>Joke</th>
<th>HaHa</th>
<th>Boohoo</th>
</tr>
</thead>
<tbody id="result">
<!-- javascript generated data -->
</tbody>
</table>
Constant variables are declared here with keyword const
Key things to know:
- The document object "result" represents table body in the HTML above.
- If you want to access any element in an HTML page in JavaScript, you always start by accessing the document object. In this case, we are accessing "result" and defining a "resultContainer"
- In the code, in following cells, document elements are created and organized for each Joke, each is added to the "resultContainer" as a row in the table body.
- Accessing the api is done using the variables url and options, this is setup to fetch the Jokes from the backend
// prepare HTML defined "result" container for new output
const resultContainer = document.getElementById("result");
// keys for joke reactions
const HAHA = "haha";
const BOOHOO = "boohoo";
// prepare fetch urls
const url = "https://flask.nighthawkcodingsociety.com/api/jokes";
const like_url = url + "/like/"; // haha reaction
const jeer_url = url + "/jeer/"; // boohoo reaction
// prepare fetch GET options
const options = {
method: 'GET', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'default', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'omit', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
};
// prepare fetch PUT options, clones with JS Spread Operator (...)
const put_options = {...options, method: 'PUT'}; // clones and replaces method
The below code uses a function called fetch to gather the data from the backend.
Key things to understand:
- The "url" "response" is checked in case the site is down and returns an error
- On successful fetch, the code places each Joke in the HTML table body using a "for" loop and creating document elements from each "row" of the fetched "data".
- The creation of each Haha and Boohoo "onclick" "button" is also done in the same loop.
- Updates to backend are setup to occur with each onclick, each click calls the "reaction" function
// fetch the API
fetch(url, options)
// response is a RESTful "promise" on any successful fetch
.then(response => {
// check for response errors
if (response.status !== 200) {
error('GET API response failure: ' + response.status);
return;
}
// valid response will have JSON data
response.json().then(data => {
console.log(data);
for (const row of data) {
// make "tr element" for each "row of data"
const tr = document.createElement("tr");
// td for joke cell
const joke = document.createElement("td");
joke.innerHTML = row.id + ". " + row.joke; // add fetched data to innerHTML
// td for haha cell with onclick actions
const haha = document.createElement("td");
const haha_but = document.createElement('button');
haha_but.id = HAHA+row.id // establishes a HAHA JS id for cell
haha_but.innerHTML = row.haha; // add fetched "haha count" to innerHTML
haha_but.onclick = function () {
// onclick function call with "like parameters"
reaction(HAHA, like_url+row.id, haha_but.id);
};
haha.appendChild(haha_but); // add "haha button" to haha cell
// td for boohoo cell with onclick actions
const boohoo = document.createElement("td");
const boohoo_but = document.createElement('button');
boohoo_but.id = BOOHOO+row.id // establishes a BOOHOO JS id for cell
boohoo_but.innerHTML = row.boohoo; // add fetched "boohoo count" to innerHTML
boohoo_but.onclick = function () {
// onclick function call with "jeer parameters"
reaction(BOOHOO, jeer_url+row.id, boohoo_but.id);
};
boohoo.appendChild(boohoo_but); // add "boohoo button" to boohoo cell
// this builds ALL td's (cells) into tr (row) element
tr.appendChild(joke);
tr.appendChild(haha);
tr.appendChild(boohoo);
// this adds all the tr (row) work above to the HTML "result" container
resultContainer.appendChild(tr);
}
})
})
// catch fetch errors (ie Nginx ACCESS to server blocked)
.catch(err => {
error(err + " " + url);
});
The below code uses fetch to update backend data using "put_options". The purpose is to update Hahaa and Bohoo counters in backend.
Key things to understand:
- The "url" "response" is checked to verify update occurred
- The element id of button clicked is updated with the data returned from the API.
- Note, the elemID is received as parameter. This data was setup when the button was created in former cell.
// Reaction function to likes or jeers user actions
function reaction(type, put_url, elemID) {
// fetch the API
fetch(put_url, put_options)
// response is a RESTful "promise" on any successful fetch
.then(response => {
// check for response errors
if (response.status !== 200) {
error("PUT API response failure: " + response.status)
return; // api failure
}
// valid response will have JSON data
response.json().then(data => {
console.log(data);
// Likes or Jeers updated/incremented
if (type === HAHA) // like data element
document.getElementById(elemID).innerHTML = data.haha; // fetched haha data assigned to haha Document Object Model (DOM)
else if (type === BOOHOO) // jeer data element
document.getElementById(elemID).innerHTML = data.boohoo; // fetched boohoo data assigned to boohoo Document Object Model (DOM)
else
error("unknown type: " + type); // should never occur
})
})
// catch fetch errors (ie Nginx ACCESS to server blocked)
.catch(err => {
error(err + " " + put_url);
});
}
// Something went wrong with actions or responses
function error(err) {
// log as Error in console
console.error(err);
// append error to resultContainer
const tr = document.createElement("tr");
const td = document.createElement("td");
td.innerHTML = err;
tr.appendChild(td);
resultContainer.appendChild(tr);
}
<!-- HTML table fragment for page -->
<table>
<thead>
<tr>
<th>Time</th>
<th>All-time Cases</th>
<th>Recorded Deaths</th>
<th>Active Cases</th>
</tr>
</thead>
<tbody>
<td id="time"></td>
<td id="total_cases"></td>
<td id="total_deaths"></td>
<td id="active_cases"></td>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Country</th>
<th>All-time Cases</th>
<th>Recorded Deaths</th>
<th>Active Cases</th>
</tr>
</thead>
<tbody id="result">
<!-- generated rows -->
</tbody>
</table>
<!-- Script is layed out in a sequence (no function) and will execute when page is loaded -->
<script>
// prepare HTML result container for new output
const resultContainer = document.getElementById("result");
// prepare fetch options
const url = "https://flask.nighthawkcodingsociety.com/api/covid/";
const headers = {
method: 'GET', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'default', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'omit', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
};
// fetch the API
fetch(url, headers)
// response is a RESTful "promise" on any successful fetch
.then(response => {
// check for response errors
if (response.status !== 200) {
const errorMsg = 'Database response error: ' + response.status;
console.log(errorMsg);
const tr = document.createElement("tr");
const td = document.createElement("td");
td.innerHTML = errorMsg;
tr.appendChild(td);
resultContainer.appendChild(tr);
return;
}
// valid response will have json data
response.json().then(data => {
console.log(data);
console.log(data.world_total)
// World Data
document.getElementById("time").innerHTML = data.world_total.statistic_taken_at;
document.getElementById("total_cases").innerHTML = data.world_total.total_cases;
document.getElementById("total_deaths").innerHTML = data.world_total.total_deaths;
document.getElementById("active_cases").innerHTML = data.world_total.active_cases;
// Country data
for (const row of data.countries_stat) {
console.log(row);
// tr for each row
const tr = document.createElement("tr");
// td for each column
const name = document.createElement("td");
const cases = document.createElement("td");
const deaths = document.createElement("td");
const active = document.createElement("td");
// data is specific to the API
name.innerHTML = row.country_name;
cases.innerHTML = row.cases;
deaths.innerHTML = row.deaths;
active.innerHTML = row.active_cases;
// this builds td's into tr
tr.appendChild(name);
tr.appendChild(cases);
tr.appendChild(deaths);
tr.appendChild(active);
// add HTML to container
resultContainer.appendChild(tr);
}
})
})
// catch fetch errors (ie ACCESS to server blocked)
.catch(err => {
console.error(err);
const tr = document.createElement("tr");
const td = document.createElement("td");
td.innerHTML = err;
tr.appendChild(td);
resultContainer.appendChild(tr);
});
</script>
This code is about HTML and it builds a table and you need the part in the beginning because it is put into a JavaScript Cell, but needs to be HTML in order to properly build the table and really show what exactly it needs to do in order to make the table. This really just sets up the code for the table so later the segments can format the data and help it ot understand what exactly is going on.
< table > makes a table, < tr > makes a row, < th > makes a column header, and < thead > is tag for the group header, and < tbody > is a tag used later to define an element id
<!-- HTML table fragment for page -->
<table>
<thead>
<tr>
<th>Time</th>
<th>All-time Cases</th>
<th>Recorded Deaths</th>
<th>Active Cases</th>
</tr>
</thead>
<tbody>
<td id="time"></td>
<td id="total_cases"></td>
<td id="total_deaths"></td>
<td id="active_cases"></td>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Country</th>
<th>All-time Cases</th>
<th>Recorded Deaths</th>
<th>Active Cases</th>
</tr>
</thead>
<tbody id="result">
<!-- generated rows -->
</tbody>
</table>
This part of the code basically just preparing the code and the table before the code actually fetches the API and outputs it as well as formats it. It is basically just a sequence. I do not know exactly what each line of code does, but I thin this whole cell is just making the headers for the table so later segments of code can reference it and use it to make a table.
<!-- Script is layed out in a sequence (no function) and will execute when page is loaded -->
<script>
// prepare HTML result container for new output
const resultContainer = document.getElementById("result");
// prepare fetch options
const url = "https://flask.nighthawkcodingsociety.com/api/covid/";
const headers = {
method: 'GET', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'default', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'omit', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
};
I think this is the base code and the main part of the program that really shows what is going on and really explains what and how the API is being formatted. First it will fetch the API, and then it will organize it into headers. Before proceeding it checks for any errors and if there are no errors then it will probably run the top part with the if statement. After that it will take the data from the API and start the organization process. This also puts data and data.world_total in the console log. It also takes each specific part of the API and chooses how to format it. It grabs specific parts of the data and this helps the code to realize where specifically the code goes in the table and what exactly to do with it. Also for each time the code makes a row depending on the data, it sends the message row to the console. The last part kind of appends some child nodes so that way it follows a specific data system and puts the information of each cell along the rows.
// fetch the API
fetch(url, headers)
// response is a RESTful "promise" on any successful fetch
.then(response => {
// check for response errors
if (response.status !== 200) {
const errorMsg = 'Database response error: ' + response.status;
console.log(errorMsg);
const tr = document.createElement("tr");
const td = document.createElement("td");
td.innerHTML = errorMsg;
tr.appendChild(td);
resultContainer.appendChild(tr);
return;
}
// valid response will have json data
response.json().then(data => {
console.log(data);
console.log(data.world_total)
// World Data
document.getElementById("time").innerHTML = data.world_total.statistic_taken_at;
document.getElementById("total_cases").innerHTML = data.world_total.total_cases;
document.getElementById("total_deaths").innerHTML = data.world_total.total_deaths;
document.getElementById("active_cases").innerHTML = data.world_total.active_cases;
// Country data
for (const row of data.countries_stat) {
console.log(row);
// tr for each row
const tr = document.createElement("tr");
// td for each column
const name = document.createElement("td");
const cases = document.createElement("td");
const deaths = document.createElement("td");
const active = document.createElement("td");
// data is specific to the API
name.innerHTML = row.country_name;
cases.innerHTML = row.cases;
deaths.innerHTML = row.deaths;
active.innerHTML = row.active_cases;
// this builds td's into tr
tr.appendChild(name);
tr.appendChild(cases);
tr.appendChild(deaths);
tr.appendChild(active);
// add HTML to container
resultContainer.appendChild(tr);
}
})
})
I think this code just tries to refetch the API if something does not work. I am not completely sure, but my best guess is that it just uses that and then creates elements based on what it has in the API. It will make a constant number of rows and cells to format the data into.
// catch fetch errors (ie ACCESS to server blocked)
.catch(err => {
console.error(err);
const tr = document.createElement("tr");
const td = document.createElement("td");
td.innerHTML = err;
tr.appendChild(td);
resultContainer.appendChild(tr);
});
</script>KeyboardEvent