Making HTTP Requests in JavaScript

by kirupa   |   20 August 2015

As you probably know very well by now, the internet is made up of a bunch of interconnected computers called servers. When you are surfing the web and navigating between web pages, what you are really doing is telling your browser to request information from any of these servers. It kinda looks as follows: your browser sends a request, waits awkwardly for the server to respond to the request, and (once the server responds) processes the request. All of this communication is made possible because of something known as the HTTP protocol.

The HTTP protocol provides a common language that allows your browser and a bunch of other things to communicate with all the servers that make up the internet. The requests your browser makes on your behalf using the HTTP protocol are known as HTTP requests, and these requests go well beyond simply loading a new page as you are navigating. A common (and whole lot more exciting!) set of use cases revolve around updating your existing page with data resulting from a HTTP request.

For example, you may have a page where you'd like to display some information about the currently logged-in user. This is information your page might not have initially, but it will be information your browser will request as part of you interacting with the page. The server will respond with the data and have your page update with that information. All of this probably sounds a bit abstract, so I'm going to go a bit weird for a few moments and describe what a HTTP request and response might look like for this example.

To get information about the user, here is our HTTP request:

GET /user
Accept: application/json

For that request, here is what the server might return:

200 OK
Content-Type: application/json

{
    "name": "Kirupa",
    "url": "http://www.kirupa.com"
}

This back and forth happens a bunch of times, and all of this is fully supported in JavaScript! This ability to asynchronously request and process data from a server without requiring a page navigation/reload has a term. That term is Ajax (or AJAX if you want to shout). This acronym stands for Asynchronous Javascript and XML, and if you were around web developers a few years ago, Ajax was the buzzword everybody threw around for describing the kind of web apps we take for granted today - apps like Twitter, Facebook, Google Maps, Gmail, and more that constantly fetch data as you are interacting with the page without requiring a full page reload!

Knowing how to Ajax it up and make HTTP requests is a very important skill, and this tutorial will give you everything you need to be dangerous. Or, should I say...dangeresque?

Onwards!

The Example

Reading (or even thinking) about the HTTP and requests is boring...extremely boring! To help you both stay awake as well as understand what all is involved, we are going to be building a small example together. The example will look as follows:

On the surface, this example seems just as boring as the underlying details of an HTTP request that I was hoping to make seem more exciting. Like, what are you going to do with this knowledge about your IP?

What this example hides is some of the awesome underlying details relevant to what you are about to learn. Here is a sneak peek. We have some JavaScript that makes an HTTP request to a service (ipinfo.io) that returns a whole bunch of data about your connection. Using JavaScript, we process all that returned data and surgically pinpoint the IP address that we so proudly display here.

I don't know about you, but I'm totally excited to see this all come together. By the time you reach the end of this tutorial, you too will have created something similar to this example and learned all about what goes on under the hood to make it work.

Meet XMLHttpRequest

In JavaScript, the object that is responsible for allowing you to send and receive HTTP requests is the weirdly named XMLHttpRequest. This object allows you to do several things that are important to making web requests. It allows you to:

  1. Send a request to a server
  2. Check on the status of a request
  3. Retrieve and parse the response from the request
  4. Listen for the onreadystatechange event that helps you react to the status of your request

There are a few more things that XMLHttpRequest does, and we'll cover them eventually. For now, these four will do just fine. Next, let's set the stage for re-creating the earlier example. All you need is just need a blank HTML page with script element inside it.

Here is what my starting point looks like:

<!DOCTYPE html>
<html>

<head>
  <title>Display IP Address</title>
</head>

<body>

  <script>

  </script>
</body>

</html>

You can go with whatever starting point you want, for we won't be doing any DOM manipulation or trying to create something visually stunning. We are just going to be adding lines of code inside the script tag. The end result of what we do will be simply displayed inside an alert statement. Of course, we will talk through each step as we get through it.

Creating the Request

The first thing we are going to do is initialize our XMLHttpRequest object, so add the following line inside your script tag:

var xhr = new XMLHttpRequest();

The xhr variable will now be the gateway to all the various properties and methods the XMLHttpRequest object provides for allowing you to make web requests. One such method is open. This method is what allows you to specify the details of the request you would like to make, so let's add it next:

var xhr = new XMLHttpRequest();
xhr.open('GET', "https://ipinfo.io/json", true);

The open method takes three-ish arguments:

  1. The first argument specifies which HTTP method to use to process your request. The values you can specify are GET, PUT, POST, and DELETE. In our case, we are interested in receiving information, so the first argument we specify is going to be GET.
  2. Next, you specify the URL to send your request to. These URLs are well-defined endpoints that know what to do when a HTTP request flies by. For our IP example, the path we will specify is ipinfo.io/json.
  3. The last argument specifies whether you want your request to run asynchronously or not. Unless you really dislike users of your app or site, always keep this value at true. Running the request asynchronously will ensure your page is responsive and the rest of your code continues to run while your HTTP request is taking its time to make its way around.
  4. The -ish part of the three-ish arguments I mentioned earlier refer to the arguments for username and password. Typically, you don't want to specify your username and password in such a plain-as-daylight-to-see location like your JavaScript file, so you probably won't ever need to set more than the three arguments you've already seen.

Sending the Request

So far, what we've done is initialized the XMLHttpRequest object and constructed our request. We haven't sent the request out yet, but that is handled by the next line:

var xhr = new XMLHttpRequest();
xhr.open('GET', "https://ipinfo.io/json", true);
xhr.send();

The send method is responsible for sending the request. If you set your request to be asynchronous (and why wouldn't you have?!!), the send method immediately returns and the rest of your code continues to run. That's the behavior we want.

What if you are making a synchronous request?

Now, you could set your request to run synchronously by specifying false for the third argument in the open method:

var xhr = new XMLHttpRequest();
xhr.open('GET', "https://ipinfo.io/json", false);
xhr.send();

When you do this, your code will stop at this point until your request has been fully processed. All UI interactions would stop. Your page will look like it is frozen. These are not good things, so to reiterate what I mentioned in the previous section, you should keep your requests asynchronous.

Asynchronous Stuff and Events

When some code is running asynchronously, you have no idea when that code is going to return with some news. In the case of what we've done, once the HTTP request has been sent, our code doesn't stop and wait for the request to make its way back. Our code just keeps running. What we need is a way to send our request and then be notified of when the request comes back so that our code can finish what it started.

To satisfy that need, that's why we have events. More specifically for our case, that's why we have the readystatechange event that is fired by our XMLHttpRequest object whenever your request hits an important milestone on its epic journey.

To set this all up, go ahead and add the following highlighted line that invokes the almighty addEventListener:

var xhr = new XMLHttpRequest();
xhr.open('GET', "https://ipinfo.io/json", true);
xhr.send();

xhr.addEventListener("readystatechange", processRequest, false);

This line looks like any other event listening code you've written a bunch of times. We listen for the readystatechange event on our xhr object and call the processRequest event handler when the event gets overheard. Here is where some fun stuff happens!

Processing the Request

This should be easy, right? We have our event listener all ready, and all we need is the processRequest event handler where we can add some code to read the result that gets returned. Let's go ahead and first add our event handler:

var xhr = new XMLHttpRequest();
xhr.open('GET', "https://ipinfo.io/json", true);
xhr.send();

xhr.onreadystatechange = processRequest;

function processRequest(e) {

}

Next, all we need is some code to parse the result of the HTTP request inside our newly added event handler...

As it turns out, it isn't that simple. The complication comes from the readystatechange event being tied to our XMLHttpRequest object's readyState property. This readyState property chronicles the path your HTTP request takes, and each change in its value results in the readystatechange event getting fired. What exactly is our readyState property representing that results in its value changing so frequently? Check out the following table:

Value State Description
0 UNSENT The open method hasn't been called yet
1 OPENED The send method has been called
2 HEADERS_RECEIVED The send method has been called and the HTTP request has returned the status and headers
3 LOADING The HTTP request response is being downloaded
4 DONE Everything has completed

For every HTTP request that you make, your readyState property hits each of these five values. This means your readystatechange event gets fired five times. As a result, your processRequest event handler gets called five times. See where the problem is? Four out five times processRequest gets called, it won't be getting called for the reasons we are interested in - that is, the request has returned and its time to analyze the returned data.

Since our goal is to read the returned value after our request has been completed, the readyState value of 4 is our friend. We need to ensure we only move forward when this value is set, so here is what the modified processRequest function would look like to handle that:

function processRequest(e) {
    if (xhr.readyState == 4) {
		// time to partay!!!
    }
}

While this seems good, we have one more check to add. It is possible for you to find yourself with no readable data despite your HTTP request having completed successfully. To guard against that, we also have HTTP status codes that get returned as a part of the request. You run into these HTTP status codes all the time. For example, whenever you see a 404, you know that a file is missing. You can see a full list of status codes if you are curious, but the one we care about with HTTP requests is status code 200. This code is returned by the server when the HTTP request was successful.

What we are going to do is modify our earlier code slightly to include a check for the 200 status code, and the appropriately named status property contains the value returned by the request:

function processRequest(e) {
    if (xhr.readyState == 4 && xhr.status == 200) {
		// time to partay!!!
    }
}

In plain English, what this check does is simple. This if statement checks that the request has completed (readyState == 4) AND is successful (status == 200). Only if both of those conditions are met can we declare that our request did what we wanted it to do.

Processing the Request...for Realz!

In the previous section, we looked at a whole lot of words for adding a simple if statement. I promise this section will be more to the point. All that is left is to read the body of the response that is returned. The way we do that is by reading the value of the responseText property returned by our xhr object:

function processRequest(e) {
    if (xhr.readyState == 4 && xhr.status == 200) {
        var response = JSON.parse(xhr.responseText);
        alert(response.ip);
    }
}

Here is where things become a bit less general. We make a request to the ipinfo.io server, and the data gets returned in JSON form...as a string:

"{
  "ip": "52.41.128.211",
  "hostname": "static-52-41-128-211.blve.wa.verizon.net",
  "city": "Redmond",
  "region": "Washington",
  "country": "US",
  "loc": "46.6104,-121.1259",
  "org": "Verizon, Inc",
  "postal": "98052"
}"

To convert our JSON-like string into an actual JSON object, we pass in the result of xhr.responseText into the JSON.parse method. This takes our string of JSON data and turns it into an actual JSON object that is stored by the response variable. From there, displaying the IP is as easy as what is shown in the highlighted line:

function processRequest(e) {
    if (xhr.readyState == 4 && xhr.status == 200) {
        var response = JSON.parse(xhr.responseText);
        alert(response.ip);
    }
}

I am not going to spend too much time on this section, for I don't want this to become a discussion of how the ipinfo.io server returns data. Every server you send a HTTP request to will send data in a slightly different way, and they may require you to jump through some slightly different hoops to get at what you are looking for. There isn't an easy solution that will prepare you for all of your future HTTP requesting needs.

What you can rely on is that the responseText property will return the raw string-based data for you to further process. Whether the string-based data needs to be converted into a JSON object like in our example, into a XML object, into an array, etc. depends entirely on what data the server returns. Just be sure to read the server's documentation on what to expect when the HTTP request returns some data. If you are like me and hate reading documentation, just inspect the value of responseText and figure out what the right course of action should be.

Conclusion

Writing some code that makes an HTTP request and returns some data is probably one of the coolest things you can do in JavaScript. Everything you've seen here used to be a novelty that only Internet Explorer supported in the very beginning. Today, HTTP requests are everywhere. So much of the data that you see displayed in a typical page is often the result of a request getting made and processed - all without you even noticing.

If you have a question about this or any other topic, the easiest thing is to drop by our forums where a bunch of the friendliest people you'll ever run into will be happy to help you out!

THE KIRUPA NEWSLETTER

Get cool tips, tricks, selfies, and more...personally hand-delivered to your inbox!

( View past issues for an idea of what you've been missing out on all this time! )

WHAT DO YOU THINK?

NEWSLETTER

No spam. No fluff. Just awesome content sent straight to your inbox!

Awesome and high-performance web hosting!
BACK TO TOP
new books - yay!!!