Exemplo prático Axios Interceptor consumindo várias APIs


Exemplo prático de como configurar o Axios Interceptor consumindo várias APIs

Configurando o serviço

  • Arquivo services/apis.tsx

import axios, { AxiosInstance } from "axios";

type ICustomInstace = AxiosInstance & {
  defaults: {
    customBaseURL?: string;
  };
};

export const instance: ICustomInstace = axios.create({});

export const apiExemplo1 = () => {
  instance.defaults.baseURL = process.env.EXPO_PUBLIC_API_EXEMPLO1;
  return instance;
};

export const apiExemplo2 = () => {
  instance.defaults.baseURL = process.env.EXPO_PUBLIC_API_EXEMPLO2;
  return instance;
};

export const apiExemplo3 = () => {
  instance.defaults.baseURL = process.env.EXPO_PUBLIC_API_EXEMPLO3;
  return instance;
};

Criando componente funcional

  • Arquivo services/apis.interceptor.tsx como um componente funcional:

import { useAuth } from "@/hooks/useAuth";
import { useEffect } from "react";
import { apiAuth, instance } from "./apis";
import { storage } from "./storage";

const AxiosInterceptor = ({ children }) => {
  const { setToken, setRefreshToken, setTokenDecoded, setRefreshTokenDecoded } =
    useAuth();
  const navigation = useNavigation();

  useEffect(() => {
    // Intercepta a requisição (request)
    instance.interceptors.request.use((request) => {
      return request;
    });

    // Intercepta a resposta (response)
    instance.interceptors.response.use(
      (response) => {
        return response;
      },
      async (error) => {
        const originalReq = error.config;
        if (
          error.response.status === 401 &&
          originalReq &&
          !originalReq._retry
        ) {
          originalReq._retry = true;

          const refresh = storage.getString("tokenRefresh");

          if (!refresh) {
            return Promise.reject(error);
          }

          const storagedTokenRefresh = storage.getString("tokenRefreshExp");
          const currentTime = new Date();
          const timeExpiryRefreshToken = new Date(storagedTokenRefresh);

          // Se o token expirar, navega p/ tela de sessão expirada
          if (timeExpiryRefreshToken && currentTime >= timeExpiryRefreshToken) {
            navigation.navigate("SessionExpired");
            return Promise.reject(error);
          }

          return apiAuth()
            .get("/tokenrefresh", {
              headers: { Authorization: refresh },
            })
            .then(async (responseRefresh) => {
              const newAccessToken = responseRefresh.data.accesstoken;
              const newRefreshToken = responseRefresh.data.refreshtoken;

              const newTokenDecoded = jwtDecode(newAccessToken) as JWTPayload;
              const newRefreshTokenDecoded = jwtDecode(
                newRefreshToken
              ) as JWTPayloadRefresh;

              const newTimeExpireJWT = newRefreshTokenDecoded.exp;
              const newTimeExpire = new Date(
                newTimeExpireJWT * 1000
              ).toString();

              storage.set("token", newAccessToken);
              storage.set("tokenRefresh", newRefreshToken);
              storage.set("tokenRefreshExp", newTimeExpire);

              setToken(newAccessToken);
              setRefreshToken(newRefreshToken);
              setTokenDecoded(newTokenDecoded);
              setRefreshTokenDecoded(newRefreshTokenDecoded);

              originalReq.headers["Authorization"] = newAccessToken;

              return instance(originalReq);
            })
            .catch((refreshError) => {
              return Promise.reject(refreshError);
            });
        } else {
          return Promise.reject(error);
        }
      }
    );
  }, []);

  return children;
};

export default AxiosInterceptor;

Envolvendo a aplicação

  • Envolva o arquivo de entrada do app o com componente AxiosInterceptor:

import * as React from "react";

export default function App() {
  return (
    <AuthProvider>
      <AxiosInterceptor>
        <StatusBarCustom />
        <Routes />
      </AxiosInterceptor>
    </AuthProvider>
  );
}

Criando uma tela de sessão expirada

  • Tela sessão expirada:

...
import { BackHandler, View } from "react-native";
import { Button, Text } from "react-native-paper";

export default function SessionExpired({ route }) {
  const { t } = useTranslation();
  const currentRoute = useRoute();

  const {
    setUser,
    setToken,
    setRefreshToken,
    setTokenDecoded,
    setRefreshTokenDecoded,
  } = useAuth();

  // Limpa dados do storage e direciona pra tela de login
  async function clearAllSession() {
		storage.clearAll();
    setUser(null);
    setToken(null);
    setRefreshToken(null);
    setTokenDecoded(null);
    setRefreshTokenDecoded(null);
  }

  // Ação botão "voltar" físico Android
  useFocusEffect(
    useCallback(() => {
      const onBackPress = () => {
        if (route.name === currentRoute.name) {
          clearAllSession();
          return true;
        } else {
          return false;
        }
      };
      BackHandler.addEventListener("hardwareBackPress", onBackPress);
      return () =>
        BackHandler.removeEventListener("hardwareBackPress", onBackPress);
    }, [route]),
  );

  return (
    <ImgBackground>
      <Container>
        <ContainerSurface>
          <View className="items-center justify-center gap-y-4">
            <LottieSessionExpired />
            <Text className="font-title text-2xl">
              {t("session-expired.title")}
            </Text>
            <Text className="font-body">
              {t("session-expired.description")}
            </Text>
            <Button
              mode="contained"
              icon="logout"
              className="w-[300]"
              onPress={() => {
                clearAllSession();
              }}
            >
              {t("session-expired.button")}
            </Button>
          </View>
        </ContainerSurface>
      </Container>
    </ImgBackground>
  );
}