Intro to Network Requests

By the end of this lesson you should…

  • Understand the difference between synchronous and asynchronous operations
  • Be familiar with the fetch API
  • Understand how network requests work
  • Know what GET and POST requests do and how to use them

Vocab

  • Async / Asynchronous Executing code without blocking the execution of code after it
  • AJAX Updating a webpage based on data from the network without reloading the whole thing

What is a network request?

Open up your dev tools and navigate to the Network tab. Refresh the page and watch what happens.

network dev tool example

Each item on a webpage is coming from some server somewhere. The link tags in your HTML connecting your stylesheets and JavaScript files prompt network requests.

Types of Requests

Network requests can be made to GET information from a server, but it’s not the only use.

The HTTP protocol defines a variety of types of requests we can make. These include:

  • GET - retrieve information from a server
  • POST - send information to a server, creating / updating resources
  • PUT - send information to a server, creating / updating resources
  • DELETE - remove information from a server
  • And many others

While getting comfortable with network requests, we’re going to focus on GET and POST requests.

Responses

Every request we make, successful or not, will receive a response.

Take another look at the Network tab in the dev tools.

network request response codes in dev tools

We can see that each request has a different response code. The HTTP protocol lays a series of Response Codes to give more information on the status of a request.

Here’s a high level overview of different statuses:

1XX status codes have informational purposes
2XX indicates success
3XX is for redirection
4XX represent client-side errors
5XX indicate problems on the server side

Common statuses:

  • 200 OK – successful request
  • 201 Created– successful POST request
  • 400 Bad Request – The request failed due to some error in its structure
  • 404 Not Found – The request was correctly structured, but specified a non-existent resource
  • 500 Internal Server Error – Something wrong happened on the server’s side of things

Making a request

Remember, every resource we use on our webpages has to get requested from some server.

Why is it important to keep this in mind?

Each network request takes time - they’re expensive. Imagine if you had to wait for a webpage to load one thing at a time! It would not make for a great user experience.

Network requests are expensive no matter what we do. However, we can run them asynchronously, saving some time.

Asynchronous operations refer to things that can happen outside the normal order of execution. Network requests can be synchronous or asynchronous, but most modern applications do them asynchronously to improve performance / the user experience.

We’ll come back to this idea, but know that when we make requests, we typically do them asynchronously. For now, lets look at how to actually make a request.

One of the more common ways to make a request is through a process called AJAX, or Asynchronous JavaScript And XML. AJAX was a huge advancement for the web, as it allowed developers to update part of a webpage without reloading the entire thing.

Traditionally, Ajax requests have been made via the XMLHttpRequest object . However, the process is a little clunky, so developers made a more streamlined way to do the same thing: the fetch API.

ES6: fetch()

Another great tool to help with network requests is the fetch API. This is what we’ll focus on in Mod 2, since we can use it “for free” with ES6 (as opposed to $.get which requires us to bring in jQuery)!

It’s important to note that not every browser supports the fetch api; polyfills are available, but many legacy codebases use other apis that are supported by older browsers, such as Axios or Superagent.

From the docs:

The fetch() method takes one mandatory argument, the path to the resource you want to fetch. It returns a promise that resolves to the Response to that request, whether it is successful or not.

Fetch will always return a promise

fetch takes only one mandatory argument: the url of the resource we are requesting.

For example:

fetch("https://opentdb.com/api.php?amount=1&category=27&type=multiple");

This make a basic GET request to the Trivia API.

fetch can also take an optional init object as its second argument.

fetch syntax

fetch(resourceUrl, {/*init object with `method`, `body`, and other optional properties*/});
// Returns a promise

Let’s dive deeper into fetch by looking at how we can make different requests with it

GET with fetch

By default, fetch performs a GET request. This means that if we only add a resource url to the fetch call, we’ll try and GET information the resource leads us to.

Try typing this in your console and see what we get back:

fetch("https://opentdb.com/api.php?amount=1&category=27&type=multiple");

If we put this in the console, we’ll see something like this:

Promise {<pending>};

Promises - the quick version

A Promise is an object that represents the eventual completion of an action.

We don’t need to worry too much about them now. Just know that a Promise will either be resolved upon completion, or rejected upon failure. We can use special methods for promises to determine what needs to happen in either of those scenarios:

  • .then() runs upon the resolution of a promise. Returns another promise
  • .catch() runs upon the rejection of a failed promise. Used for error handling

Diving into the returned promise reveals some information, such as its status and value, but nothing that’s too immediately useful. Instead we have to resolve it:

fetch("https://opentdb.com/api.php?amount=1&category=27&type=multiple");
	.then(response => console.log(response))

If you plug the code above into your console, you should see the Response object come back!

However, there’s one problem: we can’t seem to get the data we want from the Response.body. There’s one more step to parse the data (much like you do when pulling things from localStorage). We’ll need to use the Body.json() method that comes with fetch to parse it and call another .then(). (See code snippet below)

From the docs, the .json() method returns “A promise that resolves with the result of parsing the body text as JSON. This could be anything that can be represented by JSON — an object, an array, a string, a number…”

In short, it gives us access to the data!

fetch("https://opentdb.com/api.php?amount=1&category=27&type=multiple")
  .then(data => data.json())
  .then(data => console.log(data));

Lastly, we can add in a .catch() to account for any errors we may run into.

fetch("https://opentdb.com/api.php?amount=1&category=27&type=multiple")
  .then(data => data.json())
  .then(data => console.log(data))
  .catch(err => /* do something else */);

Practice

Using the Trivia API, do the following in your console:

  • Fetch 10 science questions using fetch and console.log the entire response
  • Fetch 20 geography questions and for each trivia console.log the answer only
  • Fetch 20 geography questions and console.log the response status code.

POST with fetch

What if we want to add information to a database?

If we want to use fetch to make any other kind of request, we’ll have to add an optional init object into the function call.

fetch(url, {
	method:"POST",
})

From here, the implementation may look different based on the API you’re communicating with. Some good init object properties to be aware of:

  • method - whatever kind of request we’ll be making; “GET”, “POST”, “DELETE”, etc…
  • body - the body of whatever we want to send to the server
  • headers - extra information needed about the request. Takes an object. An important header property to know:
    • Content-Type - specify what format the body will be in

Here’s a typical POST request structure:

fetch(url, {
	method: 'POST',
	headers: {
		'Content-Type': 'application/json'
	},
	body: JSON.stringify(someDataToSend), // remember how HTTP can only send and receive strings, just like localStorage?
})
	.then(response => response.json())
	.then(json => /*do something with json*/)
	.catch(err => /*do something with the error*/);

Remember, fetch still returns a promise. We’ve got to resolve it, regardless of what request type we’re making.

Often times, if a POST is successful, you’ll see a 201 created status message in the response

Practice

Head to this repo for some practice with GETting and POSTing.

Nice to Know: Query Strings / URL Structure

url anatomy diagram

What’s all that weird stuff in the URL we’re fetching?

Fetch and XMLHttpRequest Objects take the url as one of their arguments. The URL itself can be thought of containing sub-arguments that give these request objects and methods more information. The entire anatomy of a URL can be broken down into a series of informative peices, but the ones we’re focused on today are queries.

Anything coming after the ? in a url is part of a query. Queries can be broken down into categories and arguments (check the vocab here). Each category / argument pair is separated by an &.

In the example from above:

fetch("https://opentdb.com/api.php?amount=1&category=27&type=multiple")

we’re querying information about the amount, category, and type of the trivia we want to receive.

Take a look at the trivia docs, and figure out what each of the queries in our fetch request mean.


Promises

“A Promise is an object representing the eventual completion or failure of an asynchronous operation”

In our case, we can think of Promises as a placeholder that will do something once it receives a response back from the trivia server.

The great thing about promises is that since they are just objects we can move them around like an object and can return them from functions.

function getTrivia(number, categoryId) {
	const root = 'https://opentdb.com/api.php';
	const url = `${root}?amount=${number}&category=${categoryId}&type=multiple`;
	const promise = fetch(url)
	                .then(data => data.json());

	return promise;
}

getTrivia(10, 27)
.then(data => console.log(data))
.catch(err => /* do something else */);

What is this asynchronous thing all about?

Let’s say we’re at a restaurant for a night out on the town…Here’s how the experience would go in each scenario:

  • Synchronous: I order my food, everyone in the restaurant has to wait until I get my food before the next person can order.

  • Asynchronous: I order my food, the order is put into a queue, other food is made in the meantime, my food is ready, and the server brings it to me.

A Non-AJAX Example: setTimeout()

console.log("Legen...");

setTimeout(() => {
  console.log("DARY!");
}, 2000);

console.log("Wait for it...");

setTimeout() is actually an asynchronous function, which executes its callback after waiting for the allotted time to expire.

Questions:

  • Why are async operations necessary?
  • Have you run into a situation on past projects where you needed async operations to accomplish it?

Further Reading:

Lesson Search Results

Showing top 10 results