Mastering Axios - The Complete Guide to the Modern HTTP Client for JavaScript
Making and Handling API Requests with Axios
Axios has become the go-to HTTP client for web developers building modern JavaScript applications. Its promise-based interface, configurable defaults, interceptors and robust feature set make Axios flexible for diverse use cases.
In this comprehensive guide, you'll learn everything you need to use Axios like a pro. We'll cover making requests, handling responses, configuration, error handling, advanced usage with interceptors, and more.
By the end, you'll understand why Axios is so widely preferred and have the skills to integrate it into your web projects. Let's get started!
Introduction to Axios
Axios is a promise-based HTTP client for browser and Node.js. It provides an easy-to-use API for making HTTP requests and handling responses via Promises.
Some key features of Axios include:
Make GET, POST, PUT, DELETE requests
Configure defaults like baseURL, headers, timeout
Intercept requests and responses
Transform request and response data
Cancel requests
Automatic transforms for JSON data
Client-side support for protecting against XSRF
Axios was created by Matt Zabriskie in 2014 and is maintained by Matt and contributors on GitHub.
Axios works in both browser and Node.js environments. It supports older browsers and provides the polyfills needed for ES6 Promise support.
You can install Axios via npm:
npm install axios
Then import it in your JavaScript code:
// ES6 module import
import axios from 'axios';
// CommonJS require
const axios = require('axios');
Let's see a sample code :
// Get data
const response = await axios.get('/api/users');
// Post data
const response = await axios.post('/api/users', {
firstName: 'John'
});
// Error handling
try {
await axios.get('/invalid');
} catch(error) {
console.log(error);
}
// Global defaults
axios.defaults.baseURL = 'https://api.website.com';
Now let's look at making requests with Axios!
Making API Requests
The most common thing you'll do with Axios is make HTTP requests to APIs. Axios makes this very simple.
GET Request
To make a GET request, use the axios.get() method:
const response = await axios.get('/users');
Pass the URL of the API endpoint as the first argument. This returns a Promise which resolves with the response object.
Using async/await lets you write asynchronous code that reads like synchronous code. The response variable will contain the API response.
The response also contains the request object with info about the request.
POST Request
Making a POST request is just as easy. Use axios.post() and pass the URL, then the request data:
const response = await axios.post('/users', {
firstName: 'John',
lastName: 'Doe'
});
This makes a POST request to /users and sends the user data as JSON.
You can POST data in other formats like Form URL Encoded by setting the headers. But JSON is most common for APIs.
PUT, DELETE, PATCH
Axios provides shorthand methods for all HTTP verbs:
axios.get('/users');
axios.post('/users');
axios.put('/users/1234');
axios.delete('/users/1234');
axios.patch('/users/1234');
// etc
So making any kind of API request is straightforward with Axios.
Axios Global Defaults
Axios lets you configure default settings for all requests like BASE URL, headers, timeout, and more.
Set these global defaults when initializing Axios:
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.timeout = 2500;
Now every request will use that base URL, auth header, and timeout unless overridden.
Axios Instance Defaults
You can also create axios instances with preconfigured defaults:
// Set config defaults when creating the instance
const api = axios.create({
baseURL: 'https://api.example.com'
});
// Inherits 'https://api.example.com'
api.get('/users');
Instances are handy for organizing axios config - like for different APIs.
Configuring Requests
The axios request methods accept a config object which lets you specify per-request settings:
const response = await axios.get('/users', {
params: {
limit: 10 // query string params
},
timeout: 5000, // 5 second timeout
headers: {
'X-Custom-Header': 'foobar'
}
});
These settings override the global defaults for one request.
Concurrency and Queuing
Axios allows multiple requests to run concurrently by default. The adapter determines the level of concurrency based on the environment.
You can use axios.all() to execute parallel requests:
const [usersRes, postsRes] = await axios.all([
axios.get('/users'),
axios.get('/posts')
]);
The responses arrive in the same order as requested.
Axios also provides axios.spread() to use the responses:
const [users, posts] = await axios.spread((usersRes, postsRes) => {
return [usersRes.data, postsRes.data]
})
For queuing rather than concurrency, use a library like p-queue.
That covers the fundamentals of making API requests with Axios!
Here is a simple example of using Axios in React, Vue, and Angular:
// React
import axios from 'axios';
const fetchData = async () => {
const response = await axios.get('/api/data');
// update component state
}
// Vue
import axios from 'axios';
async fetchData() {
const response = await axios.get('/api/data');
// update component data
}
// Angular
import { HttpClient } from '@angular/common/http';
constructor(private http: HttpClient) {}
getData() {
return this.http.get('/api/data');
}
Next, let's look at working with the responses.
Handling API Responses
Once an API request is made, we need to handle the response. Axios response objects contain all the data we need to process responses:
const {
data,
status,
statusText,
headers,
config,
request
} = response;
data - Response payload as JSON
status - HTTP response code (200, 404, etc.)
statusText - HTTP status text ('OK', 'Not Found', etc.)
headers - Response headers object
config - Request configuration object
request - Actual HTTP request object
Let's look at some key aspects of handling responses.
Accessing Response Data
The response payload from APIs is typically JSON data. Axios parses this automatically and provides it on the data property:
// GET /users API route
const response = await axios.get('/users');
const users = response.data; // array of user objects
For binary data like images, Axios responds with a Buffer instead of parsing.
You can also parse data manually by setting responseType.
Status Codes
API responses contain useful status codes for success, errors, redirects etc.
Axios exposes the HTTP response code on response. status:
const response = await axios.get('/users');
if (response.status == 200) {
// Success case
} else if (response.status == 404) {
// Not Found error
}
Use status codes to check for success, identify errors, and implement error-handling flows.
Response Headers
Inspect the response headers for additional metadata via response. headers:
const response = await axios.get('/users');
console.log(response.headers['Content-Type']) // application/json
Headers contain info like content type, caching settings, tokens etc.
Config & Request
The response also contains the original request info that generated it:
const response = await axios.post('/users', {
firstName: 'John'
});
const requestData = response.config.data; // {firstName: 'John'}
The config contains request settings like URL, method, headers, and data.
Inspecting the request helps understand what produced the response.
// Basic Auth
axios.get('/profile', {
auth: {
username: 'john',
password: 'secret'
}
});
// Bearer token
axios.defaults.headers.common['Authorization'] = 'Bearer <token>';
// OAuth
axios.get('/data', {
headers: {
Authorization: 'Bearer <access_token>'
}
});
Processing Responses
Once you have the response data, headers, status etc. you can process it as needed:
const response = await axios.get('/users');
// Show response data on page
showUsersOnPage(response.data);
// Handle errors
if (response.status !== 200) {
handleError(response);
}
// Extract Link header
const links = parseLinkHeader(response.headers.link);
Common tasks include displaying data, error handling, pagination, saving state responses, and more.
Configuring axios
Now that we've made requests and handled responses, let's look at some powerful configuration options provided by Axios.
Axios allows setting tons of options globally or per request:
Global axios defaults
Per-request config overrides
Axios instance defaults
Axios interceptors
Let's explore the options...
Global Axios defaults
Set global configurations using Axios.defaults:
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.timeout = 2500;
Defaults are useful for:
BASE URL of API
Authentication headers
Default timeouts
Default headers like Accept, Content-Type etc.
Now these settings apply to every request.
Per-request config
Override the defaults by passing a config object when making the request:
// Defaults to https://api.example.com
axios.defaults.baseURL = 'https://api.example.com';
// Override baseURL for this request
const response = await axios.get('/users', {
baseURL: 'http://localhost:3000'
});
Common overrides include:
baseURL - Alternate API endpoint
timeout - Request specific timeout
headers - Special headers for one request
responseType - Buffer, JSON, text etc.
Axios instances
Axios lets you create reusable instances with preconfigured defaults:
// Instance with common config
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 3000
});
// Use it like default axios
api.get('/users') // https://api.example.com/users
Instances are great for:
Organizing axios config
Separate projects - like microservices
Different environments - dev vs prod
Just create an instance and use it like normal Axios.
Interceptors
Axios provides powerful interceptors for global pre and post-processing of requests & responses:
// Add auth token to all requests
axios.interceptors.request.use(config => {
config.headers.common['Authorization'] = AUTH_TOKEN;
return config;
});
// Log errors
axios.interceptors.response.use(null, error => {
console.log(error);
return Promise.reject(error);
});
Interceptors are useful for:
Processing headers
Logging
Error handling
Transforming data
Authentication
Metrics
And much more!
That covers the major Axios configuration options.
// Environment-specific defaults
const api = axios.create({
baseURL: process.env.API_URL
});
// Client-specific defaults
const api = axios.create({
headers: {
'X-Client': '123'
}
});
// Dynamic headers
axios.interceptors.request.use(config => {
config.headers.type = getFileType(); // dynamic
return config;
});
Real-world examples:
- Add global authentication header with interceptors:
// Set auth token for all requests
axios.interceptors.request.use(config => {
config.headers.common['Authorization'] = AUTH_TOKEN;
return config;
});
- Transform response data to custom format:
// Convert snake_case to camelCase
axios.interceptors.response.use(res => {
res.data = convertToCamelCase(res.data);
return res;
});
Next, let's handle errors which are inevitable when working with HTTP requests.
Error Handling in Axios
When working with external APIs and networks, errors are bound to happen. Axios makes handling errors straightforward.
The cleanest way is using async/await and standard try/catch:
async function getUsers() {
try {
const response = await axios.get('/users');
console.log(response.data);
} catch (error) {
console.log(error);
// handle error here
}
}
Alternatively, provide callbacks to .then() and .catch():
axios.get('/users')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error.response); // full response
console.log(error.request); // request object
});
We have multiple options and rich data for handling errors.
Network Errors
Network errors and failures occur when the request doesn't complete:
{
// Request failed to complete
message: 'Network Error',
request: {...}, // Request object
response: undefined // Response wasn't received
}
This could happen from network issues, server going down, CORS problems, timeouts etc.
The best practice is to retry failed requests 1-3 times with backoff.
Validation Errors
APIs may return 4xx errors like 400 Bad Request for invalid data:
{
// Request completed but server responded with error
message: 'Validation Failed',
response: {
status: 400,
data: {
errors: ['Invalid email']
}
}
}
For validation errors, show the error message, but don't retry.
Server Errors
5xx errors indicate server-side issues:
{
// Server had an internal error
message: 'Internal Server Error',
response: {
status: 500,
data: 'Something broke!'
}
}
Retry 5xx errors 1-3 times before giving up. Monitor for spikes in server errors.
HTTP Status Codes
Axios rejects the Promise for HTTP errors so we can handle them in catch():
try {
const response = await axios.get('/invalid-url');
} catch (error) {
if (error.response) {
// HTTP error from server
if (error.response.status === 404) {
alert('Page Not Found!');
}
} else {
// Network error
alert('Network Error');
}
}
Using the status code, you can handle different scenarios properly.
Global Error Handling
For consistent error handling globally, use interceptors:
// Global app error handler
axios.interceptors.response.use(null, error => {
if (error.response) {
// HTTP client errors
handleClientError(error);
} else {
// Network errors
handleNetworkError(error);
}
return Promise.reject(error);
});
function handleClientError(error) {
// Handle 4xx-5xx errors
}
function handleNetworkError(error) {
// Retry or notify user
}
This avoids duplicate error-handling logic throughout the app.
To sum-up :
// Jest example
import axios from 'axios';
import { mockResponse } from './mocks';
jest.mock('axios');
test('fetch users', async () => {
axios.get.mockResolvedValue(mockResponse);
const response = await axios.get('/users');
expect(response.data).toEqual(mockResponse.data);
});
That covers a variety of approaches for handling errors with Axios!
Advanced Usage
Axios has many advanced features for customizing requests, automation, transitions, and more. Let's look at some advanced usage next.
Transforming Data
Transform request data before sending, and response data before receiving:
// Transform request data
axios.interceptors.request.use(config => {
config.data = transformRequest(config.data);
return config;
})
// Transform response data
axios.interceptors.response.use(res => {
res.data = transformResponse(res.data);
return res;
})
function transformRequest(data) {
// transform...
return data;
}
function transformResponse(data) {
// transform...
return data;
}
This allows normalizing data to the format backends or frontends expect.
Cancellation
Cancel in-flight requests using request cancellation tokens:
const cancelToken = axios.CancelToken.source();
axios.get('/users', {
cancelToken: cancelToken.token
});
// Cancel the request
cancelToken.cancel('Operation canceled by user');
Aborting requests is useful for typeahead search UIs, route changes, etc.
Axios Instances
Create custom axios instances for different use cases:
const api = axios.create({
baseURL: '/api'
});
const longTimeoutApi = axios.create({
timeout: 10000 // 10 seconds
});
// Override timeouts as needed
longTimeoutApi.get('/long-request');
Instances keep code modular. Just import the preconfigured instance for that scenario.
interceptors
Axios interceptors allow monitoring and transforming requests & responses:
// Log all requests
axios.interceptors.request.use(config => {
console.log('Making request to', config.url);
return config;
});
// Retry requests
axios.interceptors.response.use(null, error => {
return retryRequest(error.config);
});
// Redirect on 401 errors
axios.interceptors.response.use(null, error => {
if (error.response.status === 401) {
router.push('/login');
}
return Promise.reject(error);
});
Interceptors are extremely powerful!
This covers some of the more advanced features provided by Axios.
Improving Frontend Performance
When using Axios in the browser, there are additional considerations for performance best practices.
Here are some tips for optimizing requests for fast, responsive web apps:
Debounce/throttle rapid requests - avoid firing many at once
Avoid giant responses - paginate and limit response size
Enable HTTP caching with headers like Cache-Control
Preload requests early with
Use conditional requests with If-Modified-Since
Load non-critical resources lazily/on demand
Minify response JSON
Compress larger responses with gzip via Accept-Encoding
Use Service Workers to cache requests
Prioritize visible/critical resources
Set timeouts to prevent long delays
Following performance best practices will ensure using Axios doesn't negatively impact web app speed and user experience.
Lets conclude this with final example :
// Mocking
import mockAdapter from 'axios-mock-adapter';
const mock = new mockAdapter(axios);
// Caching
import cacheAdapter from 'axios-extensions';
// Error handling
import { axiosRetry } from 'axios-retry';
// Upload progress
import { axiosProgress } from 'axios-progress-bar';
Conclusion
And that wraps up this comprehensive guide to Axios!
We covered making requests, handling responses, configuration, error handling, and advanced usage of this awesome library.
You should now have a deep understanding of how to integrate Axios into your apps and harness its full power.
The key takeaways are:
Easily make requests with GET, POST, PUT, DELETE and more
Use interceptors for request and response transformation
Handle errors gracefully with built-in tools
Configure globally or per request
Leverage response data, headers, status codes
Optimize performance best practices
Understand alternatives like Fetch API and jQuery
Axios provides an amazing developer experience for working with HTTP in JavaScript. I hope you found this guide useful! Let me know if you have any other questions.
Happy coding!