React Best Practices

Prefer Axios over fetch

Making a case for Axios

In many applications we are using low level fetch methods and making custom adjustments as we need. It works but Axios can abstract away most of the stuff that's done manually there. If we use Axios, we can handle these cases much more cleanly.

For example, Axios interceptors are much easier to set up. Also, when we import Axios, we receive a singleton. So, whatever change we make will be reflected anywhere we use Axios. T his is useful while handling headers. When logging in, we can set auth tokens in Axios headers so that we won't have to specify it for every requests. When logging out, we can reset the Axios headers.

Why use Axios?

Axios helps to keep things D.R.Y with instances, interceptors, and defaults. This can help with complex applications that are frequently making API requests.

  • Interceptors: Access the request or response configuration (headers, data, etc) as they're outgoing or incoming. These functions can act as gateways to see configuration or add/modify data.
  • Instances: Create reusable instances with baseUrl, headers, and other configuration already set up.
  • Defaults: Set default values for common headers (like Authorization) on outgoing requests. this will be useful if you're authenticating to a server on every request.

Separate instances are often created for various APIs, interceptors are set up for global error handling, and defaults are often set, or unset, for things like common headers.

Axios’s library has a few other useful features.

  1. Handling timeout
  2. Intercept and/or Cancel requests
  3. Protection against XSRF
  4. Automatic transforms for JSON data
  5. Wide support for browsers

Install

1$ yarn add axios

Usage

1axios.get('/users/12345')
2  .then(response => {
3    // handle success
4    console.log(response);
5  })
6  .catch(error => {
7    // handle error
8    console.log(error);
9  })
10  .then(() => {
11    // always executed
12  });

Setup

It is suggested to add following configurations in componentDidMount of App.jsx so that the axios instance is prepared to be used throughout the app.

1. Default config

1import axios from 'axios';
2import { getCSRFToken } from 'components/utils';
3
4axios.defaults.baseURL = 'https://example.com/api/v1/';
5axios.defaults.timeout = 1000;
6axios.defaults.headers.common['X-CSRF-Token'] = getCSRFToken();
7
8axios.get('/users/12345')
9  .then(response => {
10    console.log(response);
11  });

2. Create an instance with default config

This could be useful for making different API calls which may not be done from the default instance. For example: External services etc.

1import axios from 'axios';
2import { getCSRFToken } from 'components/utils';
3
4const API = axios.create({
5  baseURL: 'https://example.com/api/v1/',
6  timeout: 1000,
7  headers: {
8    'X-CSRF-Token': getCSRFToken()
9  }
10});
11
12API.get('/users/12345')
13  .then(response => {
14    console.log(response);
15  });

3. Intercept requests to add auth headers

1const requestHandler = request => {
2  request.headers['Authorization'] = getAuthHeader();
3
4  return request;
5}
6
7axios.interceptors.request.use(request => requestHandler(request));

4. Intercept responses to log errors

1// Provide only response json part
2// => Chuk other metadata provided by axios
3const responseSuccessHandler = response => {
4  return response.data;
5};
6
7// Log & Sanitize errors response
8// => The errors given by server will not be always consistent so we
9//    could sanitize the response and return proper error to the client.
10const responseErrorHandler = error => {
11  var errors = ["Something went wrong, please try again!"];
12
13  if (error.response) {
14    if (error.response.data.errors)
15      errors = error.response.data.errors;
16    if (error.response.data.error)
17      errors = [error.response.data.error];
18
19    if (error.response.status === 401)
20      forceLogoutUser();
21  } else if (error.request) {
22    console.log(error.request);
23  } else {
24    console.log('Error', error.message);
25  }
26
27  return Promise.reject({
28    status: error.response.status,
29    errors: errors
30  });
31}
32
33axios.interceptors.response.use(
34  response => responseSuccessHandler(response),
35  error => responseErrorHandler(error)
36);

Little things that matter

Axios simplifies and abstracts away a lot of code for us. Here are some useful abstractions that axios provides to us.

1. Track upload progress

Axios is based on XMLHttpRequest and consequently all the events, properties and methods that come along with it are available in Axios. A well known fact(nitpick) about fetch() is that it doesn't provide a method to track upload progress. Axios provides very easy ways to track upload progress on the other hand.

Here is an example from the axios repo itself:

1const [uploadProgress, setUploadProgress] = useState(0); // display uploadProgress value in a loader
2// other lines of code
3// .......
4
5const config = {
6   onUploadProgress: progressEvent => {
7     const percentCompleted = Math.round( (progressEvent.loaded * 100) / progressEvent.total );
8     setUploadProgress(percentCompleted);
9   }
10 };
11
12 try {
13   const updatedBlog = axios.put('/upload/server', data, config);
14   return updatedBlog.response.data;
15 } cactch(err) {
16   if(!err.response) {
17     throw err;
18   }
19   throw err.response.data;
20 }

2. Set timeout

As shown earlier setting timeout is as easy as adding a timeoout: 3000 key-value pair to the axios config object. fetch() provides the AbortController interface for timeouts, but it is not as simple to implement as in axios. You can also implement your own higher-order function and make the fetch call inside the returned function.

References

  • Kent C. Dodds' opinion on when to prefer which among axios and fetch.
⌘K
    to navigateEnterto select Escto close
    Previous
    Next