Antonio José
Antonio José

Reputation: 495

Error using axios interceptors and responses

I'm using "axios": "^0.23.0", with ReactJs and Typescript.

I want to intercept requests and responses and add the user's token.

When I try to use the request interceptor, I get the following error:

Object is possibly 'undefined'.  TS2532
(property) AxiosRequestConfig<any>.headers?: AxiosRequestHeaders | undefined

           (config) => {
             const user = getUserLocalStorage();
             config.headers.Authorization = user?.token;
             ^
             return config;
           },
           (error) => {

This error is occurring when I add the .Authorization to

config.headers.Authorization = user?.token;

What should I do to fix this error?

These are the methods I'm using:

api.ts

import axios from "axios";
import { getUserLocalStorage } from "../context/AuthProvider/util";

export const Api = axios.create({
  baseURL: "http://localhost:8000/",
});

Api.interceptors.request.use(
  (config) => {
    const user = getUserLocalStorage();
    config.headers.Authorization = user?.token;
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
)

util.ts

import { Api } from "../../services/api";
import { IUser } from "./types";

export function setUserLocalStorage (user: IUser | null) {
  localStorage.setItem('u', JSON.stringify(user));
}

export function getUserLocalStorage () {
  const json = localStorage.getItem('u');

  if (!json) {
    return null;
  }

  const user = JSON.parse(json);

  return user ?? null;
}

export async function LoginRequest (username: string, password: string) {
  try {
    const request = await Api.post(
      'login/', 
      {username, password}
    );
    return request.data;

  } catch (error) {
    return null;
  }
}

index.tsx

import React, {createContext, useEffect, useState} from "react";
import { IAuthProvider, IContext, IUser } from "./types";
import { getUserLocalStorage, LoginRequest, setUserLocalStorage } from "./util";

export const AuthContext = createContext<IContext>({} as IContext)

export const AuthProvider = ({children}: IAuthProvider) => {
  const [user, setUser] = useState<IUser | null>()

  useEffect(() => {
    const user = getUserLocalStorage();

    if (user) {
      setUser(user);
    }
  }, [])

  async function authenticate(
    username:string, 
    password: string
  ) {
    let response: any;
    response = await LoginRequest(username, password);
    const payload = {token: response.token};

    setUser(payload);
    setUserLocalStorage(payload);
  }

  function logout () {
    setUser(null);
    setUserLocalStorage(null);
  }

  return (
    <AuthContext.Provider value={{...user, authenticate, logout}}>
      {children}
    </AuthContext.Provider>
  )
}

Thanks!

Upvotes: 3

Views: 15303

Answers (2)

Rui Nunes
Rui Nunes

Reputation: 61

Actually, you shouldn't redefine the headers object on the interceptor.

Instead, you should check if the headers object exists or not, before adding the new authorization property.

So, instead of this:

Api.interceptors.request.use(
    (config) => {
        const user = getUserLocalStorage();

        // This might cause issues because the `headers` object is always being redefined here - previously passed headers will be discarded
        config.headers = {
            Authorization: user?.token,
        };

        return config;
    },
    (error) => {
        return Promise.reject(error);
    }
);

you should do this:

Api.interceptors.request.use(
    (config) => {
        const user = getUserLocalStorage();

        if(!config.headers) {
            config.headers = {};
        }

        config.headers.Authorization = user?.token;

        return config;
    },
    (error) => {
        return Promise.reject(error);
    }
);

This way you ensure you're not ignoring/overriding the headers object that might come from the axios.{post/get/delete/etc}(url, config) calls.

Upvotes: 6

Antonio Jos&#233;
Antonio Jos&#233;

Reputation: 495

The way of assigning the token value must be changed. Change:

config.headers.Authorization = user?.token;

for:

config.headers = {
    Authorization: user?.token,
};

So, it should look like this:

export const Api = axios.create({
    baseURL: "http://localhost:8000/",
});

Api.interceptors.request.use(
    (config) => {
        const user = getUserLocalStorage();

        config.headers = {
            Authorization: user?.token,
        };

        return config;
    },
    (error) => {
        return Promise.reject(error);
    }
);

Upvotes: 7

Related Questions