When it comes to modern application development, whether that be web, mobile, or other, there is almost always a need interact with remote web services, generally through HTTP. When working with frameworks such as Angular, Vue, and React, there is baked in functionality for making requests, but what about if you’re using vanilla JavaScript or you’d prefer not to use those built in functionalities?
In this tutorial we’re going to explore a few options towards making HTTP requests in JavaScript. Particularly we’re going to focus on the classic XHR request, using a modern JavaScript Fetch, as well as using a third-party package called Axios.
When deciding how you want to make HTTP requests in JavaScript, it is more of a preference thing. Each offers its own strengths, but at their own weaknesses as well.
If you’ve been working with JavaScript for a long time, you’ve probably heard XMLHttpRequest
(XHR) come up in regards to making requests. The XHR object is quite powerful because you can request data from a remote resource, receive data from a remote resource, or send data to a remote resource. All of what I said might sound like the same thing, but they are different parts of the HTTP process.
So let’s take a look at what one of these requests might look like:
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if(xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText);
}
};
xhr.open("GET", "http://httpbin.org/get", true);
xhr.send();
In the above example we are creating a new XMLHttpRequest
object. We need to work with the change listener if we want to do something with the result of the request. In our example, we are checking to make sure the request is complete, hence the readyState
value equalling 4, and that we received a success code as our status.
The four different readyState
values can be found in the MDN documentation for XHR requests. As you can imagine there are quite a few possible status codes that can be returned from a server. In a realistic scenario, you’d probably want to accommodate all of the possibilities for the best user experience.
For this example, the response of the request is just printed to the logs.
When opening the request, the URL is defined and whether or not you want to send it as an asynchronous operation or not.
Now let’s complicate the XHR request a bit. Let’s do the following:
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if(xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.responseText);
}
};
xhr.open("POST", "http://httpbin.org/post?key=abc123", true);
xhr.send(JSON.stringify({ "firstname": "Nic", "lastname": "Raboy" }));
In the above code, we are now doing a POST request with query parameters and a request body. Notice that we have to url-encode the query parameters ourself as well as serialize the request body. It isn’t the end of the world, but it can be a pain sometimes.
The XMLHttpRequest
is powerful, but in my opinion more trouble than it is worth. This leads us to more modern approaches to HTTP requests with JavaScript.
I didn’t find out about the fetch
function in JavaScript until recently. I had strictly been using third-party libraries or built in framework options to get the job done in my applications. The fetch
function takes your request information and returns a promise as the result.
Let’s take a look at what our XHR request would look like if we had chosen to use fetch
instead:
fetch("http://httpbin.org/get")
.then(response => response.json())
.then(response => {
console.log(response);
}, error => {
console.error(error);
});
Instead of having to worry about creating listeners or worrying about response status codes, we can check to see if the promise resolved or rejected. The fetch
function will return a promise with raw data which needs to be converted to JSON, text, or similar. This is why a promise chain is used. I know the result of this particular endpoint will return JSON, so I’m converting it as such before chaining it to a new link in the promise chain.
While fetch
is plenty powerful for many situations, it can sometimes lack a certain elegance. Let’s change some things in our request so it looks like the following:
fetch("http://httpbin.org/post?key=abc123", {
"method": "POST",
"body": JSON.stringify({
"firstname": "Nic",
"lastname": "Raboy"
})
})
.then(response => response.json())
.then(response => {
console.log(response);
});
In the above request, we are changing from GET to POST and we are supplying a request body as well as query parameters. Notice the query parameters are inline directly with the URL and the request body is a serialized JSON object. It works, but it isn’t the most attractive.
To improve things, we can turn towards a third-party library to be used in our JavaScript applications.
One of my favorite libraries for making HTTP requests is the axios library. In fact, I wrote about it in my tutorial titled, Consume Remote API Data via HTTP in a Vue.js Web Application, which was around the Vue JavaScript framework.
Because this solution isn’t baked into modern iterations of JavaScript, it needs to be imported into your project. If you’re using NPM, you can execute the following command:
npm install axios --save
The above command will work great if you’re using Node.js or a modern framework that leverages Webpack or similar, but it isn’t much help when building a basic browser based application with vanilla JavaScript. Instead, you can include the library with the following tag:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
Since this is a basic JavaScript example, the <script>
tag is fine, just don’t forget to import the library within your JavaScript file if you’re using the NPM approach.
Within your code, you can make use of axios by including the following:
axios({
"method": "GET",
"url": "http://httpbin.org/get"
}).then(response => {
console.log(response.data);
});
The above code is the axios version of using the fetch
function or an XMLHttpRequest
. Like with the fetch
function, the axios approach returns a promise.
Now you might be wondering why it makes any sense to use axios at all considering this particular request is no more difficult than the fetch
request. The answer is, for this example it doesn’t make any sense, but if you have a more complicated request, then you might become interested.
Take the following for example:
axios({
"method": "POST",
"url": "http://httpbin.org/post",
"params": {
"key": "abc123"
},
"data": {
"firstname": "Nic",
"lastname": "Raboy"
}
}).then(response => {
console.log(response.data);
});
Notice that we’ve changed the request method to a POST request this time around. We’ve decided that we want query parameters in the URL which were added through the params
object and a request body that was added to the data
object. We didn’t have to url-encode these parameters or serialize the request body, making an elegant solution, particularly when you have large request bodies or a lot of query parameters. You can even add header information as well.
You just saw a few options for making HTTP requests in JavaScript applications. The approaches mentioned, in some circumstances, should work beyond web applications. This means that you should be able to use the approaches mentioned in browser based applications, backend applications, cross-platform mobile applications, and more.
As of lately my preferred approach has been to use the fetch
function, but in times where a more complicated request is required, I’ll switch over to a third-party library like axios.
If you’re looking for a Node.js specific example, you might want to wander over to my previous tutorial titled, Consume Remote API Data from within a Node.js Application, which makes use of the request package.
A video version of this tutorial can be seen below.