2

I want to create a global axios instance with an interceptor which adds a header with access token. The access token is stored in a cookie, but I can't figure out how to access the cookie inside the interceptor as it is not a react component.

I have tried creating the axios instance in a custom hook like this:

import React, { useEffect, useState } from "react";
import { useCookies } from "react-cookie";
import axios from "axios";

function useApiClient() {
  const [cookies, setCookie] = useCookies(["token"]);
  const [client, setClient] = useState(null);

  useEffect(() => {
    setClient(
      axios
        .create({
          baseURL: "http://localhost:8080/api/",
          responseType: "json",
        })
        .interceptors.request.use(
          (config) => {
            console.log("setting up");
            if (cookies["token"] != null) {
              config.headers.authorization = cookies["token"];
            }
          },
          (error) => Promise.reject(error)
        )
    );
  }, [client]);

  return client;
}

export default useApiClient;

But when I try to call this using:

const client = useApiClient();

function attemptLogin() {
    const credentials = {
      username: username,
      password: password,
    };

    client
      .post("/authenticate", JSON.stringify(credentials), {
         headers: {
          "Content-Type": "application/json",
         },
       }).then(....)

I get below error:

TypeError: client.post is not a function

Can anyone help?

3
  • One problem I notice is that you need to client dependency like this in an array: [client] in the useEffect. Also can you show how do you use client.post? Commented Apr 6, 2020 at 11:48
  • Hi @SuleymanSah Than you for your comment. I have tried adding the dependency in an array, but no luck. I have edited my question to show how i'm using the client. Commented Apr 6, 2020 at 11:58
  • have you considered the option of withCredentials: true for the axios config? Commented Apr 13, 2020 at 23:05

2 Answers 2

5
+25

The problem is that you are trying to use react-cookie that provides an api for working with cookies in components. It may be fine for some cases, but for the case when you need to check cookies on every request it forces putting an axios instance somewhere at the top of components hierarchy and also makes it harder to use the instance out of components (most likely you'll pass the instance to outer functions).

What I suggest is to make an instance in a separate module, and use a different tool for getting cookies like universal-cookie.

// apiClient.js
import axios from "axios";
import Cookies from "universal-cookie";

const cookies = new Cookies();

export const apiClient = axios
  .create({
    baseURL: "http://localhost:8080/api/",
    responseType: "json"
  })
  .interceptors.request.use(
    config => {
      console.log("setting up");
      const token = cookies.get("token");
      if (token != null) {
        config.headers.authorization = token;
      }
    },
    error => Promise.reject(error)
  );

After that, you can use this instance everywhere in the app

import React from 'react';
import { apiClient } from "./apiClient";

export default ({ name }) => {
  useEffect(() => {
    apiClient.get(/*...*/).then(/*...*/);
  });

  return <h1>Hello {name}!</h1>;
}

If you prefer to stick to hooks you could wrap a client into one easily:

// apiClient.js
export function useApiClient() { return apiClient; } 

Playgroung with full example

Sign up to request clarification or add additional context in comments.

Comments

0

That is maybe because useEffect in useApiClient.js is async method.

Personally I see no harm in directly assigning headers to axios object. However I see you are using axios.create so I would assume you may be making calls to multiple Service APIs/endpoints and all do not need same headers. Anyways...

Suggestions

  1. Make useApiClient a simple class which returns instance of axios with required headers. If not, I am seriously missing the point of using useEffect here.

Behavior

In each file you file call this and all will have separate instances of axios without polluting the global axios object.

useApiClient.js

    function useApiClient {
        return axios
          .create({
            baseURL: "http://localhost:8080/api/",
            responseType: "json"
        })
        .interceptors.request.use(
          config => {
            console.log("setting up");
            const token = cookies.get("token");
            if (token != null) {
              config.headers.authorization = token;
            }
          },
          error => Promise.reject(error)
        );
      }
    }

    export default useApiClient;
  1. Make a Factory Class which returns axios instances based on the input parameter (to assign different headers based on parameter passed and return the instance). You may also have it as Singleton if feasible.

Hope it helps.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.