Editor’s note: Updated on 29 November 2023, this tutorial covers Axios usage with async/await
for better code maintenance and Axios error handling.
Axios is a client HTTP API based on the XMLHttpRequest
interface provided by browsers. In this tutorial, we’ll demonstrate how to make HTTP requests using Axios with clear examples, including how to make an Axios POST request with axios.post()
, how to send multiple requests simultaneously with Promise.all
, and much more.
If you’re more of a visual learner, check out the video tutorial below:
How to make HTTP requests like a pro with Axios
In this video, we’ll take a good look at Axios, a client HTTP API based on the XMLHttpRequest interface provided by browsers, and examine the key features that have contributed to its rise in popularity among frontend developers. Introduction — 00:00 Why Axios?
The most common way for frontend programs to communicate with servers is through the HTTP protocol. You are probably familiar with the Fetch API and the XMLHttpRequest
interface, which allows you to fetch resources and make HTTP requests.
If you’re using a JavaScript library, chances are it comes with a client HTTP API. jQuery’s $.ajax()
function, for example, has been particularly popular with frontend developers. But as developers move away from such libraries in favor of native APIs, dedicated HTTP clients have emerged to fill the gap.
As with Fetch, Axios is promise-based. However, it provides a more powerful and flexible feature set. Advantages of using Axios over the native Fetch API include:
You can install Axios using:
npm:
npm install axios
Yarn:
yarn add axios
pnpm:
pnpm add axios
The Bower package manager:
bower install axios
Or a content delivery network:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
Making an HTTP request is as easy as passing a config object to the axios
function. You can make a POST request using Axios to “post” data to a given endpoint and trigger events. To perform an HTTP POST request in Axios, call axios.post()
.
Making a POST request in Axios requires two parameters: the URI of the service endpoint and an object that contains the properties you wish to send to the server.
For a simple Axios POST request, the config object must have a url
property. If no method is provided, GET
will be used as the default value.
Let’s look at a simple Axios POST example:
// send a POST request axios({ method: 'post', url: '/login', data: { firstName: 'Finn', lastName: 'Williams' } });
This should look familiar to those who have worked with jQuery’s $.ajax
function. This code is instructing Axios to send a POST request to /login
with an object of key/value pairs as its data. Axios will automatically convert the data to JSON and send it as the request body.
Axios also provides a set of shorthand methods for performing different types of requests. The methods are as follows:
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])
For instance, the following code shows how the previous example could be written using the axios.post()
method:
axios.post('/login', { firstName: 'Finn', lastName: 'Williams' });
axios.post
return?Once an HTTP POST request is made, Axios returns a promise that is either fulfilled or rejected, depending on the response from the backend service.
To handle the result, you can use the then()
method, like this:
axios.post('/login', { firstName: 'Finn', lastName: 'Williams' }) .then((response) => { console.log(response); }, (error) => { console.log(error); });
If the promise is fulfilled, the first argument of then()
will be called; if the promise is rejected, the second argument will be called. According to the Axios documentation, the fulfillment value is an object containing the following properties:
{ // `data` is the response that was provided by the server data: {}, // `status` is the HTTP status code from the server response status: 200, // `statusText` is the HTTP status message from the server response statusText: 'OK', // `headers` the headers that the server responded with // All header names are lower cased headers: {}, // `config` is the config that was provided to `axios` for the request config: {}, // `request` is the request that generated this response // It is the last ClientRequest instance in node.js (in redirects) // and an XMLHttpRequest instance the browser request: {} }
As an example, here’s how the response looks when requesting data from the GitHub API:
axios.get('https://api.github.com/users/mapbox') .then((response) => { console.log(response.data); console.log(response.status); console.log(response.statusText); console.log(response.headers); console.log(response.config); }); // logs: // => {login: "mapbox", id: 600935, node_id: "MDEyOk9yZ2FuaXphdGlvbjYwMDkzNQ==", avatar_url: "https://avatars1.githubusercontent.com/u/600935?v=4", gravatar_id: "", …} // => 200 // => OK // => {x-ratelimit-limit: "60", x-github-media-type: "github.v3", x-ratelimit-remaining: "60", last-modified: "Wed, 01 Aug 2018 02:50:03 GMT", etag: "W/"3062389570cc468e0b474db27046e8c9"", …} // => {adapter: ƒ, transformRequest: {…}, transformResponse: {…}, timeout: 0, xsrfCookieName: "XSRF-TOKEN", …}
async
and await
The async
and await
syntax is syntactic sugar around the Promises API. It helps you write cleaner, more readable, and maintainable code. With async
and await
, your codebase feels synchronous and easier to think about.
When using async
and await
, you invoke axios
or one of its request methods inside an asynchronous function, like in the example below:
const fetchData = async () => { try { const response = await axios.get("https://api.github.com/users/mapbox"); console.log(response.data); console.log(response.status); console.log(response.statusText); console.log(response.headers); console.log(response.config); } catch (error) { // Handle error console.error(error); } }; fetchData();
When using the async
and await
syntax, it is standard practice to wrap your code in a try-catch block. Doing so will ensure you appropriately handle errors and provide feedback for a better user experience.
Promise.all
to send multiple requestsYou can use Axios with Promise.all
to make multiple requests in parallel by passing an iterable of promises to it. The Promise.all
static method returns a single promise object that fulfills only when all input promises have fulfilled.
Here’s a simple example of how to use Promise.all
to make simultaneous HTTP requests:
// execute simultaneous requests Promise.all([ axios.get("https://api.github.com/users/mapbox"), axios.get("https://api.github.com/users/phantomjs"), ]).then(([user1, user2]) => { //this will be executed only when all requests are complete console.log("Date created: ", user1.data.created_at); console.log("Date created: ", user2.data.created_at); }); // logs: // => Date created: 2011-02-04T19:02:13Z // => Date created: 2017-04-03T17:25:46Z
This code makes two requests to the GitHub API and then logs the value of the created_at
property of each response to the console. Keep in mind that if any of the input promises reject, then the promise will immediately reject with the reason of the first promise that rejects.
Be aware that Axios also has two built-in functions, axios.all
and axios.spread
, that are deprecated. You may encounter them frequently in legacy code, but do not use them in new projects.
The axios.all
function is similar to the Promise.all
static method described above. On the other hand, axios.spread
is a convenient method to assign the properties of the response array to separate variables. You can use array destructuring instead of axios.spread
.
Here’s how you could use axios.all
and axios.spread
:
axios.all([ axios.get('https://api.github.com/users/mapbox'), axios.get('https://api.github.com/users/phantomjs') ]) .then(axios.spread((user1, user2) => { console.log('Date created: ', user1.data.created_at); console.log('Date created: ', user2.data.created_at); })); // logs: // => Date created: 2011-02-04T19:02:13Z // => Date created: 2017-04-03T17:25:46Z
The output of this code is the same as the previous example. The only difference is that we used axios.all
and axios.spread
instead.
Sending custom headers with Axios is straightforward. Simply pass an object containing the headers as the last argument. For example:
const options = { headers: {'X-Custom-Header': 'value'} }; axios.post('/save', { a: 10 }, options);
When making a network request to a server, it is not uncommon to experience delays when the server takes too long to respond. It is standard practice to timeout an operation and provide an appropriate error message if a response takes too long. This ensures a better user experience when the server is experiencing downtime or a higher load than usual.
With Axios, you can use the timeout
property of your config
object to set the waiting time before timing out a network request. Its value is the waiting duration in milliseconds. The request is aborted if Axios doesn’t receive a response within the timeout duration. The default value of the timeout
property is 0
milliseconds (no timeout).
You can check for the ECONNABORTED
error code and take appropriate action when the request times out:
axios({ baseURL: "https://jsonplaceholder.typicode.com", url: "/todos/1", method: "get", timeout: 2000, }) .then((response) => { console.log(response.data); }) .catch((error) => { if (error.code === "ECONNABORTED") { console.log("Request timed out"); } else { console.log(error.message); } });
You can also timeout a network request using the AbortSignal.timeout
static method. It takes the timeout as an argument in milliseconds and returns an AbortSignal
instance. You need to set it as the value of the signal
property.
The network request aborts when the timeout expires. Axios sets the value of error.code
to ERR_CANCELED
and error.message
to canceled
:
const abortSignal = AbortSignal.timeout(200); axios({ baseURL: "https://jsonplaceholder.typicode.com", url: "/todos/1", method: "get", signal: abortSignal, }) .then((response) => { console.log(response.data); }) .catch((error) => { if (error.code === "ERR_CANCELED" && abortSignal.aborted) { console.log("Request timed out"); } else { console.log(error.message); } });
Axios automatically serializes JavaScript objects to JSON when passed to the axios.post
function as the second parameter. This eliminates the need to serialize POST bodies to JSON.
Axios also sets the Content-Type
header to application/json
. This enables web frameworks to automatically parse the data.
If you want to send a preserialized JSON string to axios.post()
JSON, you’ll need to make sure the Content-Type
header is set.
Although Axios automatically converts requests and responses to JSON by default, it also allows you to override the default behavior and define a different transformation mechanism. This is particularly useful when working with an API that accepts only a specific data format, such as XML or CSV.
To change request data before sending it to the server, set the transformRequest
property in the config object. Note that this method only works for PUT
, POST
, DELETE
, and PATCH
request methods.
Here’s an example of how to use transformRequest
in Axios:
const options = { method: 'post', url: '/login', data: { firstName: 'Finn', lastName: 'Williams' }, transformRequest: [(data, headers) => { // transform the data return data; }] }; // send the request axios(options);
To modify the data before passing it to then()
or catch()
, you can set the transformResponse
property:
const options = { method: 'post', url: '/login', data: { firstName: 'Finn', lastName: 'Williams' }, transformResponse: [(data) => { // transform the response return data; }] }; // send the request axios(options);
HTTP interception is a popular feature of Axios. With this feature, you can examine and change HTTP requests from your program to the server and vice versa, which is very useful for a variety of implicit tasks, such as logging and authentication.
At first glance, interceptors look very much like transforms, but they differ in one key way: unlike transforms, which only receive the data and headers as arguments, interceptors receive the entire response object or request config.
You can declare a request interceptor in Axios like this:
// declare a request interceptor axios.interceptors.request.use(config => { // perform a task before the request is sent console.log('Request was sent'); return config; }, error => { // handle the error return Promise.reject(error); }); // sent a GET request axios.get('https://api.github.com/users/mapbox') .then(response => { console.log(response.data.created_at); });
This code logs a message to the console whenever a request is sent and then waits until it gets a response from the server, at which point it prints the time the account was created at GitHub to the console. One advantage of using interceptors is that you no longer have to implement tasks for each HTTP request separately.
Axios also provides a response interceptor, which allows you to transform the responses from a server on their way back to the application:
// declare a response interceptor axios.interceptors.response.use((response) => { // do something with the response data console.log('Response was received'); return response; }, error => { // handle the response error return Promise.reject(error); }); // sent a GET request axios.get('https://api.github.com/users/mapbox') .then(response => { console.log(response.data.created_at); });
Cross-site request forgery (or XSRF for short) is a method of attacking a web-hosted app in which the attacker disguises themself as a legal and trusted user to influence the interaction between the app and the user’s browser. There are many ways to execute such an attack, including XMLHttpRequest
.
Fortunately, Axios is designed to protect against XSRF by allowing you to embed additional authentication data when making requests. This enables the server to discover requests from unauthorized locations.
Here’s how this can be done with Axios:
const options = { method: 'post', url: '/login', xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', }; // send the request axios(options);
Another interesting feature of Axios is the ability to monitor request progress. This is especially useful when downloading or uploading large files. The example provided in the Axios documentation gives you a good idea of how that can be done. But for the sake of simplicity and style, we are going to use the Axios Progress Bar module in this tutorial.
The first thing we need to do to use this module is to include the related style and script:
<link rel="stylesheet" type="text/css" href="https://cdn.rawgit.com/rikmms/progress-bar-4-axios/0a3acf92/dist/nprogress.css" /> <script src="https://cdn.rawgit.com/rikmms/progress-bar-4-axios/0a3acf92/dist/index.js"></script>
Then we can implement the progress bar like this:
loadProgressBar() const url = 'https://media.giphy.com/media/C6JQPEUsZUyVq/giphy.gif'; function downloadFile(url) { axios.get(url) .then(response => { console.log(response) }) .catch(error => { console.log(error) }) } downloadFile(url);
To change the default styling of the progress bar, we can override the following style rules:
#nprogress .bar { background: red !important; } #nprogress .peg { box-shadow: 0 0 10px red, 0 0 5px red !important; } #nprogress .spinner-icon { border-top-color: red !important; border-left-color: red !important; }
In some situations, you may no longer care about the result and want to cancel a request that’s already been sent. This can be done by using AbortController
. You can create an AbortController
instance and set its corresponding AbortSignal
instance as the value of the signal
property of the config object.
Here’s a simple example:
const controller = new AbortController(); axios .get("https://media.giphy.com/media/C6JQPEUsZUyVq/giphy.gif", { signal: controller.signal, }) .catch((error) => { if (controller.signal.aborted) { console.log(controller.signal.reason); } else { // handle error } }); // cancel the request (the reason parameter is optional) controller.abort("Request canceled.");
Axios also has a built-in function for canceling requests. However, the built-in CancelToken
functionality is deprecated. You may still encounter it in legacy codebase, but it is not advisable to use it in new projects.
Below is a basic example:
const source = axios.CancelToken.source(); axios.get('https://media.giphy.com/media/C6JQPEUsZUyVq/giphy.gif', { cancelToken: source.token }).catch(thrown => { if (axios.isCancel(thrown)) { console.log(thrown.message); } else { // handle error } }); // cancel the request (the message parameter is optional) source.cancel('Request canceled.');
You can also create a cancel token by passing an executor function to the CancelToken
constructor, as shown below:
const CancelToken = axios.CancelToken; let cancel; axios.get('https://media.giphy.com/media/C6JQPEUsZUyVq/giphy.gif', { // specify a cancel token cancelToken: new CancelToken(c => { // this function will receive a cancel function as a parameter cancel = c; }) }).catch(thrown => { if (axios.isCancel(thrown)) { console.log(thrown.message); } else { // handle error } }); // cancel the request cancel('Request canceled.');
An HTTP request may succeed or fail. Therefore, it is important to handle errors on the client side and provide appropriate feedback for a better user experience.
Possible causes of error in a network request may include server errors, authentication errors, missing parameters, and requesting non-existent resources.
Axios, by default, rejects any response with a status code that falls outside the successful 2xx range. However, you can modify this feature to specify what range of HTTP codes should throw an error using the validateStatus
config option, like in the example below:
axios({ baseURL: "https://jsonplaceholder.typicode.com", url: "/todos/1", method: "get", validateStatus: status => status <=500, }) .then((response) => { console.log(response.data); })
The error object that Axios passes to the .catch
block has several properties, including the following:
.catch(error => { console.log(error.name) console.log(error.message) console.log(error.code) console.log(error.status) console.log(error.stack) console.log(error.config) })
In addition to the properties highlighted above, if the request was made and the server responded with a status code that falls outside the 2xx range, the error object will also have the error.response
object.
On the other hand, if the request was made but no response was received, the error object will have an error.request
object. Depending on the environment, the error.request
object is an instance of XMLHttpRequest
in the browser environment and an instance of http.ClientRequest
in Node.
You need to check for error.response
and error.request
objects in your .catch
callback to determine the error you are dealing with so that you can take an appropriate action:
axios.get("https://jsonplaceholder.typicode.com/todos").catch(function (error) { if (error.response) { // Request was made. However, the status code of the server response falls outside the 2xx range console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); } else if (error.request) { // Request was made but no response received console.log(error.request); } else { // Error was triggered by something else console.log("Error", error.message); } console.log(error.config); });
However, sometimes duplicating the code above in the .catch
callback for each request can become tedious and time-consuming. You can instead intercept the error and handle it globally like so:
axios.interceptors.request.use(null, function (error) { // Do something with request error return Promise.reject(error); }); axios.interceptors.response.use(null, function (error) { // Do something with response error if (error.response) { // Request was made. However, the status code of the server response falls outside the 2xx range } else if (error.request) { // Request was made but no response received } else { // Error was triggered by something else } return Promise.reject(error); });
Axios’ rise in popularity among developers has resulted in a rich selection of third-party libraries that extend its functionality. From testers to loggers, there’s a library for almost any additional feature you may need when using Axios. Here are some libraries that are currently available:
When it comes to browser support, Axios is very reliable. Even older browsers such as IE 11 work well with Axios:
Chrome | Firefox | Safari | Edge | IE |
---|---|---|---|---|
âś” | âś” | âś” | âś” | 11 |
There’s a good reason Axios is so popular among developers: it’s packed with useful features. In this post, we’ve taken a look at several key features of Axios and learned how to use them in practice. But there are still many aspects of Axios that we haven’t discussed. Be sure to check out the Axios GitHub page to learn more.
Do you have some tips on using Axios? Let us know in the comments!
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
5 Replies to "How to make HTTP requests with Axios"
You should also note that axios can also be used on the server with node.js – probably one of my favorite higher level HTTP libraries.
One of the better qualities when using it on the server is the ability to create an instance with defaults – for example sometimes I’ll need to access another REST API to integrate another service with one of our products, if there is no existing package or the existing package doesn’t support the end points I need to access I’ll just create an abstraction which internally uses a http client created by axios.create():
const instance = axios.create({
baseURL: ‘https://api.example.org/’,
headers: {‘Some-Auth-Header’: ‘token’}
});
Cheers,
Chris
Can you please answer this?
https://stackoverflow.com/questions/58996278/how-to-send-binary-stream-from-string-content-to-third-party-api-using-axios-nod
This post says nothing about the responseType parameter, which can stream a large binary file.
Got a question about accessing the data outside of the axios.spread. What I am doing is using node to collate some data from disparate API calls and return one dataset. I do the two calls, create a new object and return it.
The new object exists within the AXIS code block but when I try and view outside it is blank.
I also tried to do this in a function with a return but it also returns a blank.
let retData = {};
axios
.all([reqDevInfo, reqConInfo])
.then(
axios.spread((resDevInfo,resConInfo ) => {
retData.status = 200;
retData.deviceName = deviceName
retData.tenant = resDevInfo.data.results[0].tenant.name;
retData.ru = resDevInfo.data.results[0].position;
retData.TServerName = resConInfo.data.results[0].connected_endpoint.device.name;
retData.TServerPort = resConInfo.data.results[0].cable.label;
console.log(retData); // this print the expected data
})
)
.catch(errors => {
// react on errors.
console.error(errors);
});
console.log(retData) // this is blank
How can I build or append data elements to a post request before I send the request?
I have optional 4 optional parameters and there are too many combinations to code for all the variations.