Intro to AJAX and XHR — Network Requests in JavaScript

Tomasz Chmielnicki
The Startup
Published in
5 min readOct 26, 2020

--

Photo by NASA on Unsplash

What is a network request

Whenever you submit a url from your web browser’s address bar, you make an HTTP request to a server. What you receive from that request is an HTML document, the website that you requested. HTTP is the protocol that allows us to do that.

What AJAX is useful for

In HTML, we can link to other documents using the a element. After clicking a link like that we get redirected to another page, specified in the href attribute. The problem is, the entire page needs to reload. What if we don’t want our users to stare at a white screen? Maybe we need to retrieve some data from the server but only want to replace a part of our page? That’s where AJAX comes in.

What is AJAX

AJAX (Asynchronous JavaScript And XML) is simply a way of using JavaScript to fetch data from the server without reloading the website. XML is a data format similar to html, but nowadays JSON is more common. JSON (JavaScript Object Notation) is data in the form of JavaScript objects. It’s the format in which we want to be receiving data from the server. Using AJAX, we can asynchronously request some data in the background with JavaScript and display it to the user once we receive it. We no longer need to reload the entire page.

Using an API

To get that data we will use something called Application Programming Interface. It is a way of communication between software. It will provide our JavaScript code with a way to make requests to a server. All we need to do in our case is to use a specific url and the server will return data to us based on that url. That’s how we can make an API call. In this example, we will be using a publicly available API to get random pictures of dogs. Pretty awesome, right?

Making requests with the XHR object.

In order to make an ajax request, we will use an XMLHttpRequest object. Let’s set up the boilerplate code. First, we want to create the object using the constructor:

const xhr = new XMLHttpRequest()

Now we need to specify the HTTP method that we are going to use. We are going to use the GET method. Basically, it means that if we were to pass any parameters to the API, we would put them directly in the url. We also need to provide the url from which we want to get the data. That’s the API.

const url = 'https://random.dog/woof.json'
const xhr = new XMLHttpRequest()
xhr.open('GET', url)

You can paste the url into your browser and see what comes up. The data that comes back to our xhr object is in the form of a string by default, but we can request an object. Let’s do that.

const url = 'https://random.dog/woof.json'
const xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'

Here comes the important part. We want to pass a callback function to our xhr object that will run when the data comes back.

const url = 'https://random.dog/woof.json'const xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = () => console.log(xhr.response)

It’s just like specifying an onclick handler. The provided function will run once the request completes. Let’s log the data that we get from the server. It’s in the response property of our xhr object.

Now, to send the request, we have to call the send method:

const url = 'https://random.dog/woof.json'const xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = () => console.log(xhr.response)
xhr.send()

Hurray! We get an object with the size of the image and the url that links to it. Obviously, logging it to the console isn’t that exciting, but i leave it up to you to make use of what you’ve learned. Hint: You can make a website where, every time you click a button, a random dog picture shows up.

XHR is asynchronous

Because a network request takes time to complete, xhr is asynchronous by default. It means that, if we try to access the result of our request before it finishes, we will get null.

const url = 'https://random.dog/woof.json'const xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = () => console.log(xhr.response)
//Callback function logs the response once the request completes
xhr.send()
console.log(xhr.response)
//Synchronous console.log runs before the asynchronous network request completes, prints null

Error handling with XHR

In a real application, when performing network requests, the user will often run into errors, for example when they lose their internet connection or when the requested recource couldn’t be found.

When a request couldn’t be sent at all, the error event runs. We can specify what to do when that happens by passing another callback:

xhr.onerror = () => console.log('There was a network error. Check your internet connection and try again.')

This will only run when the request couldn’t be made. That happens when there is no internet connection or the url is invalid.

The load event will run even if the status of the response is, for example, 404. That’s why we need to check the status inside the onload callback:

xhr.onload = () => {
if (xhr.status !== 200) {
console.log(`Error ${xhr.status}: ${xhr.statusText}`)
} else {
console.log(xhr.response)
}
}

If everything went right, the status will be 200. We can get the status code from xhr.status and the corresponding message from xhr.statusText. If there was an error, the result of our console.log would be something like “Error 404: Not found”.

This is the final code with error handling implemented:

const url = 'https://random.dog/woof.json'const xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = () => {
if (xhr.status !== 200) {
console.log(`Error ${xhr.status}: ${xhr.statusText}`)
} else {
console.log(xhr.response)
}
}
xhr.onerror = () => console.log('There was a network error. Check your internet connection and try again.')xhr.send()

Replace the url with something like “https://random.dog/woof.json/aaa” to check if the 404 is handled properly. You can also replace the url with an invalid one such as “https://aaa” to see the onerror event handler in action.

Conclusion

XHR provides us with a way of asynchronously making network requests. We set things up, provide a url, load and error handlers, then send the request. If you want to learn more about XHR, for example, how to determine the progress of the request, check out this article.

Network requests in modern JavaScript

XHR requires quite a bit of boilerplate code. We need to construct the object, set the HTTP method every time and explicitly send the request. It also relies on callback functions which can get messy in complex scenarios if not handled properly. In modern JavaScript, we use a function called fetch for network requests. It uses ES6 Promises and it’s more flexible and powerful. Check out my article on fetch and Promises to learn how to simplify network requests!

--

--