Intro to AJAX and XHR — Network Requests in JavaScript
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!