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>
);
}