creating a rest APIs using node.js express quickly and easily - part 2

 

creating a rest APIs using node.js express quickly and easily




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 asyncawaittry, 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"));
  }
});


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 awaittry, 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 andelseare replaced with the keywordstryandcatch. a condition Theawait` 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