How to I create a general global Axios instance that will use my Redux store for header token information?

Viewed 1727

I am currently using Axios to do API calls and a Redux store to keep my JWT tokens such as my access tokens.

Here is an example of what my current Axios instance looks like:

ProductionInstanceAPI.js

import axios from 'axios';

export default axios.create({
  baseURL: `https://my.app/`,
});

And it is used like this in one of my React pages:

Predictions.js

import ProductionAPI from '../../ProductionInstanceAPI';
import { useSelector } from 'react-redux';
import { selectAccount } from '../../redux/features/account/accountSlice'

const Predictions = () => {
  const accountInformation = useSelector(selectAccount);

  useEffect(() => {
    ProductionAPI.get('ml/get_linear_regressions/', { headers: {"Authorization": `Bearer ${accountInformation.accessToken}`}})
      .then((response) => {
        let data = response.data;
        console.log(data);
      }).catch(error => {
        console.log(error);
      })
    }, [accountInformation.accessToken]);
}

However thought I hate having to do useSelector for all of my .js files that do an API call and then have to add in the information to my API calls.

I tried doing this, however though you can't use the useSelector hook outside of a React component.

ProductionInstanceAPI.js trying to use Redux store

import axios from 'axios';
import { useSelector } from 'react-redux';
import { selectAccount } from './redux/features/account/accountSlice'

const accountInformation = useSelector(selectAccount)

export default axios.create({
  baseURL: `https://my.app/`,
  headers: {"Authorization": `Bearer ${accountInformation.accessToken}`}
});

How do I somehow add the accessToken from my Redux store for all of my Axios calls? Is there a way to do this that is cleaner?

Thank you so much!

1 Answers

I would strongly suggest not to go with a global set up for this situation mainly because it deals with token information which is used for authentication.

It must be stateful, that is, tied to the app. So, you would need to put it in the store OR use react context. Basically, both approaches wrap with information within the context of the App. This makes for a controlled approach to get the tokens. Pretty much simlar to what you are doing.

You can create functions which must be supplied with the token, which returns an axios instance. This is just sample code but the idea here is to create a pattern, which mandates the caller to provide the token.

Something like,

export const getAxiosInstance = (token) => {
  // create axios instance and return
}

Eventually, the consumer would always be a component so that component will have access to the store, right? So, it can take the responsibility of passing the token.

If you don't want every method to pass the token, you can pull up the whole axios instance in the react context and then make sure that the instance is updated when it should be. Then the consuming components can get the axios instance from the context. For example, this is the same pattern used in things like react query and apollo client, where they wrap the application in a provider component.

Axios also has the concept of Interceptors which you can use to boot up things and add token headers in this way. But even in this case, you should have a controlled approach as mentioned above.

If you try to find a way to expose the token in a static manner, it is always bound to cause issues. For instance, if you log out and log in and let's assume the entire interaction is on client side, you can run the risk of some process still using old token and things like that.

Another way is you can put the token in session storage. But that is not good anyway cos you don't want to put this information in session storage as it makes for easy access to tokens and promotes a static access mechanism, which we want to avoid.

Related