axios request retry fails: Cannot read property 'cancelToken' of undefined

Viewed 719

I am currently using the following api client written in Typescript:

enum StatusCode {
  Unauthorized = 401,
  Forbidden = 403,
  TooManyRequests = 429,
  InternalServerError = 500,
}

const headers: Readonly<Record<string, string | boolean>> = {
  Accept: "application/json",
  "Content-Type": "application/json",
};

const injectToken = (config: AxiosRequestConfig): AxiosRequestConfig => {
  if (
    config.baseURL === API_HOST &&
    !config.headers.Authorization &&
    useAuthStatus.getState().status === "logged-in"
  ) {
    try {
      const token = getAccessToken();

      if (token) {
        config.headers.Authorization = `Bearer ${token}`;
      }

      return config;
    } catch (error) {
      throw new Error(error);
    }
  }
};

class Http {
  private instance: AxiosInstance | null = null;

  private get http(): AxiosInstance {
    return this.instance != null ? this.instance : this.initHttp();
  }

  initHttp() {
    const http = axios.create({
      baseURL: API_HOST,
      headers,
    });

    http.interceptors.request.use(injectToken, (error) => {
      Promise.reject(error);
    });

    http.interceptors.response.use(
      (response) => {
        const { data } = response;
        return data;
      },
      (error) => {
        const { response } = error;
        return this.handleError(response);
      }
    );

    this.instance = http;
    return http;
  }

  request<T = any, R = AxiosResponse<T>>(
    config: AxiosRequestConfig
  ): Promise<R> {
    return this.http.request(config);
  }

  get<T = any, R = AxiosResponse<T>>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<R> {
    return this.http.get<T, R>(url, config);
  }

  post<T = any, R = AxiosResponse<T>>(
    url: string,
    data?: T,
    config?: AxiosRequestConfig
  ): Promise<R> {
    return this.http.post<T, R>(url, data, config);
  }

  put<T = any, R = AxiosResponse<T>>(
    url: string,
    data?: T,
    config?: AxiosRequestConfig
  ): Promise<R> {
    return this.http.put<T, R>(url, data, config);
  }

  patch<T = any, R = AxiosResponse<T>>(
    url: string,
    data?: T,
    config?: AxiosRequestConfig
  ): Promise<R> {
    return this.http.patch<T, R>(url, data, config);
  }

  delete<T = any, R = AxiosResponse<T>>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<R> {
    return this.http.delete<T, R>(url, config);
  }


  private handleError(error) {
    const { status } = error;

    switch (status) {
      case StatusCode.InternalServerError: {
        break;
      }
      case StatusCode.Forbidden: {
        // Handle Forbidden
        break;
      }
      case StatusCode.Unauthorized: {
        // Handle Unauthorized
        if (useAuthStatus.getState().status === "logged-in") {
          return refreshToken().then((_) => {
            error.config.headers.Authorization = `Bearer ${
              useTokenStore.getState().accessToken
            }`;
            return this.request(error.config);
          });
        }

        return Promise.reject(error);
      }
      case StatusCode.TooManyRequests: {
        // Handle TooManyRequests
        break;
      }
    }

    return Promise.reject(error);
  }
}

export const http = new Http();

currently I have the problem that I can't manage to repeat the request after a successful token refresh in case of a 401 error. The problem is that I call ``this.request(error.config)```. I get the following error, which I can't explain:

TypeError: Cannot read property 'status' of undefined
    at Http.handleError (http.ts?5220:118)
    at eval (http.ts?5220:62)

If I replace this.request(error.config) with axios.request(error.config), the retry works fine, but my interceptors are no longer set as it creates a new axios instance. I would like to continue the retry with the same Axios instance (http in my case).

I just see that I fall into the catch block on this interceptor and the error is as follows:

   http.interceptors.response.use(
      (response) => {
        const { data } = response;
        return data;
      },
      (error) => {
        const { response } = error;
        return this.handleError(response);
      }
    )

results in:

TypeError: Cannot read property 'cancelToken' of undefined

What could be the reason for this behavior?

0 Answers
Related