We talked about how to creating a rest APIs using node.js express quickly and easily - part 1 in a previous article.
And today we continue ...
In order for you to have a complete picture of how to creating a rest APIs using node.js express quickly and easily - part 2.
Understanding Server & Client Side Code:
Assuming we have set up a POST route in the file server.js
file, we will move into the website
folder and start writing client side
code in a file named app.js
. Here is the code we could use to make a POST request to our
route:
const postData =
async ( url =
'', data = {})=>{
console.log(data);
const response =
await fetch(url, {
method:
'POST',
credentials:
'same-origin',
headers: {
'Content-Type':
'application/json',
},
// Body data type must match "Content-Type" header
body:
JSON.stringify(data),
});
try {
const newData =
await response.json();
console.log(newData);
return newData;
}
catch(error) {
console.log(
"error", error);
}
}
postData(
'/add', {answer:
42});
Client Side & Server Side Example:
Let's focus in on the actual POST request,
which is an object passed as the second parameter to fetch()
. The First parameter is the URL we want to
make the POST request to.
{
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
body:
JSON.stringify(
data),
}
The credentials and headers are pretty
boilerplate, but necessary for a successful POST request. The most important
thing to notice is that Content-Type
is set to json because we will be
handling our data with JSON, for the most part.
Now we get to the juicy parts: the method
is set to POST
because we are accessing the POST route
we setup in server.js
. If we wanted to make a GET
request from the client side, the method
would be GET
. The body
of the request is the part we are most
interested in because this is how we will access the data on the server side.
When sending data to a web server, the data has to be a string. We can convert
a JavaScript object into a string using the JavaScript method JSON.stringify()
, which turns JavaScript objects and JSON data
into a string for our server to receive the information. In this example, we
are turning the JavaScript object passed in the data
parameter into a string.
Processing the POST request:
So now that we know how to send a POST request
from the client side, let's return to the server code and learn how to process
the data we receive with our POST request. Remember, the last line of code in
our previous example called our postData
function, passing in the URL of the POST route, and an
object containing the data to be posted. That line of code looked like
this: postData('/add', {answer:42})
Back on the server side code we should now be
able to receive the data answer:42
. Remember in the last example, we attached our data to
the body
of our POST request, so to receive that
data and make it actionable we can use request.body
.
Here is an example where we set a variable
named data
to hold the value
of request.body
, and then print data
to see what we received.
app.post(
'/add',
function (request, response) {
let data = request.body;
console.log(data);
});
The output of this would display in ther
terminal: {answer:42}
But we don't just want to see the data we
received, to complete our POST request we must assign the data we received to
our project endpoint, the JS object in our server code named projectData
. We can simply make a new entry in our JS
object using the syntax:
projectData["x"]
= y
This code would create a new entry in our JS
object API endpoint where the value of a string "x"
is y
. So if the data received from the POST
request was {intelligence:100}
, we could create a new entry in our endpoint with the
code: let data = request.body; projectData["intelligence"]= data.intelligence;
Notice that we manually set the string for the
key of the new JS object entry as "intelligence"
, and then to access the property we want to
set as its value we use data.intelligence
.
For more on JS dot notation see the MDN Web Docs entry on
Property accessors.
In this lesson we have learned how server side
and client side code work together to route data through a web app. We learned
how to setup a POST request in the server side code, and then execute a POST
request via that path on the client side code. We also covered how to structure
and add POST request data to a project API endpoint. In the next lesson we will
learn more about asynchronous functions in JavaScript and you will learn more
in depth about some of the syntax used in these demos, including async
, await
, try
,
and catch
.
For more details on server side and client
side programming see this Stack Overflow post.
Async Promises:
While there have always
been some async work arounds in JS, including setTimeout(), and AJAX, more
recently a tool called Promises has been introduced natively to JavaScript, and
Promises are now the accepted best practice for writing asynchronous functions
in JavaScript.
You can think of Promises
as a special function that either satisfy (resolve) or fail (reject) to execute
a task, and then executes the corresponding actions, usually another task with
the returned data in the case of 'resolved' and usually throw an error in the
case of 'reject'.
Here is the basic anatomy of a
Promise:
Syntax
var promise =
new
Promise(
function(resolve, reject) {
// do a thing, possibly async, then…
if (
/* everything turned out fine */) {
resolve(
"Stuff worked!");
}
else {
reject(
Error(
"It broke"));
}
});
var promise =
new
Promise(
function(resolve, reject) {
// do a thing, possibly async, then…
if (
/* everything turned out fine */) {
resolve(
"Stuff worked!");
}
else {
reject(
Error(
"It broke"));
}
});
There are many methods to handle asynchronous
work already, however Promises are the recommended option because they give you
flexibility, intuitive syntax, and easy error handling. Promises are an amazing
development in JavaScript, but until ES2017 (ES8) they still required extra
boilerplate code, called generators, to run asynchronously. Now however, with
the addition of native async
functions to JavaScript, we can easily apply the async
keywords to a Promise to execute asynchronous JavaScript code.
To make a fetch()
call, or any other methods inside of a
function, asynchronous we must use the keywords provided by JavaScript. Here is
an example of an asynchronous fetch function using JavaScript keywords:
const postData =
async ( url =
'', data = {})=>{
const response =
await fetch(url, {
method:
'POST',
// *GET, POST, PUT, DELETE, etc.
credentials:
'same-origin',
headers: {
'Content-Type':
'application/json',
},
body:
JSON.stringify(data),
// body data type must match "Content-Type" header
});
try {
const newData =
await response.json();
return newData
}
catch(error) {
console.log(
"error", error);
// appropriately handle the error
}
}
postData(
'/addMovie', {movie:
' the matrix', score:
5})
postData
is an async arrow function that is
called with parameters on the last line of code. It is asynchronous because of
the keyword async
placed before its parameters.
const postData =
async ( url =
'', data = {})=>{
const response =
await fetch(url, {
method:
'POST',
credentials:
'same-origin',
headers: {
'Content-Type':
'application/json',
},
body:
JSON.stringify(data),
});
...
}
Once you mark a function as 'async' you have
access to the keywords await
, try
,
and catch
.
try {
const newData =
await response.json();
return newData
}
catch(error) {
console.log(
"error", error);
// appropriately handle the error
}
The keywords try
and catch
mirror the Promise functionality of resolving
or rejecting to execute a task. In this case. if and
elseare replaced with the keywords
tryand
catch. a condition The
await` keyword is used in places where the next actions requires
data from the current action so we want to tell our program to wait until the
data has been received before continuing with the next steps-- this is the
magic of ASYNC JavaScript.
In the next lesson we will learn how to pair
JavaScript async fetch functions with Web APIS to unleash the dynamic power of
front-end programming.
More on Async JS:
For a more detailed overview on Promises and why they matter, read the article here.
Adding Fetch to Your Code:
Here is the client side
code that would make a GET request to the animal info API:
let baseURL =
'http://api.animalinfo.org/data/?animal='
let apiKey =
'&appid=9f15e45060...';
document.getElementById(
'generate').addEventListener(
'click', performAction);
function performAction(e){
const newAnimal =
document.getElementById(
'animal').value;
getAnimal(baseURL,newAnimal, apiKey)
}
const getAnimal =
async (baseURL, animal, key)=>{
const res =
await fetch(baseURL+animal+key)
try {
const data =
await res.json();
console.log(data)
return data;
}
catch(error) {
console.log(
"error", error);
// appropriately handle the error
}
}
Async Fetch with Web APIs Demo:
As you continue to work with Promises and
Async JavaScript it will start to feel more natural to do so. In the meantime,
you can keep checking back against the keywords and making sure you are
using async
await
try
and catch
to lead you to async dreamland.
Chaining Promises - Dependent GET and POST Requests:
Notice we have set up a
helper function to use fetch to make an async GET request for a route that is
made to simulate the Animal Info Web API we are using as an example.
Inside .then()
we could call another async function to
make a POST request to store this data in our app. Assuming a POST route has
been setup on the server side to add data it received to the app endpoint, we
could simply call the function we have been using to create POST requests on
the client side and pass it the POST route url and the data we want to save to
our app. The only tricky part (which can also be fun!), is that we need to use
the returned data, and data that we retrieve from a DOM element to create the
structure for our POST request.
As a reminder, the postData()
function takes a URL, and a data object
as parameters. To build the data object using data received from the previous
fetch call we can use dot notation. So we could set our first elements like
this:
postData('/addAnimal',
{animal:data.animal, fact: data.fact} )
But we also want to
include the users favorite thing about the animal, which we can add using the
variable name which selects the textarea where the users response is. So our
final code for creating a POST route to save the data to our app would look
like this:
postData('/addAnimal',
{animal:data.animal, fact: data.fact, fav:favFact} )
Then on the server side
to actually add the sent data to our app, we would use this code:
app.post(
'/addAnimal', addAnimal);
function addAnimal(req,res){
newEntry = {
animal: req.body.animal,
facts: req.body.fact,
fav: req.body.fav
}
animalData.push(newEntry)
console.log(animalData)
}
Great! Now let’s finally
learn how to update the UI of an app with the data gathered from requests and
routes.
Dynamic UI Updates:
Here is what it would look like to use chained
GET and POST requests to retrieve information from our animal Web API, and then
update DOM elements accordingly:
HTML
<label
for=
"animal">Enter
the
name
of your favorite animal</label>
<input
id=
"animal"
name=
"animal">
<textarea
id=
"favorite" placeholder=
"Enter your favorite thing about your favorite animal" rows=
"9" cols=
"50"></textarea>
<button
id =
"generate">GO</button>
JS
document.getElementById(
'generate').addEventListener(
'click', performAction);
function performAction(e){
const newAnimal =
document.getElementById(
'animal').value;
const favFact =
document.getElementById(
'favorite').value;
getAnimal(
'/animalData',)
// New Syntax!
.then(
function(data){
// Add data
console.log(data);
postData(
'/addAnimal', {animal:data.animal, fact: data.fact, fav:favFact} );
})
.then(
updateUI()
)
}
const updateUI =
async () => {
const request =
await fetch(
'/all');
try{
const allData =
await request.json();
document.getElementById(
'animalName').innerHTML = allData[
0].animal;
document.getElementById(
'animalFact').innerHTML = allData[
0].facts;
document.getElementById(
'animalFav').innerHTML = allData[
0].fav;
}
catch(error){
console.log(
"error", error);
}
}
Notice how calling the function to update the
UI is the last thing we do -- this is because the update UI function depends on
data from each of the other functions, so each Promise must be resolved
successfully before we can update the UI. This demonstrates why native Promises
and the Fetch API are such powerful tools for Asynchronous JavaScript.
With this, we are done with how to creating a rest APIs using node.js express quickly and easily in two parts and i hope that's the full picture of how to do that...
Comments
Post a Comment