⚡️(frontend) prevent authentication retry on 401 responses
Stop retry attempts when receiving 401 Unauthorized from /me endpoint since this clearly indicates authentication status. The original purpose of the /me call is simply to determine if user is authenticated, and a 401 provides sufficient information. Prevents unnecessary network requests caused by React Query's automatic retry behavior when re-raising exceptions, which was hiding the 401 status. Improves performance and reduces server load during authentication failures.
This commit is contained in:
committed by
Anthony LC
parent
ff8275fb4e
commit
2a7ffff96d
@@ -9,6 +9,8 @@ import { useResponsiveStore } from '@/stores/';
|
||||
|
||||
import { ConfigProvider } from './config/';
|
||||
|
||||
export const DEFAULT_QUERY_RETRY = 1;
|
||||
|
||||
/**
|
||||
* QueryClient:
|
||||
* - defaultOptions:
|
||||
@@ -19,7 +21,7 @@ import { ConfigProvider } from './config/';
|
||||
const defaultOptions = {
|
||||
queries: {
|
||||
staleTime: 1000 * 60 * 3,
|
||||
retry: 1,
|
||||
retry: DEFAULT_QUERY_RETRY,
|
||||
},
|
||||
};
|
||||
const queryClient = new QueryClient({
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { UseQueryOptions, useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||
import { DEFAULT_QUERY_RETRY } from '@/core';
|
||||
|
||||
import { User } from './types';
|
||||
|
||||
type UserResponse = User | null;
|
||||
|
||||
/**
|
||||
* Asynchronously retrieves the current user's data from the API.
|
||||
* This function is called during frontend initialization to check
|
||||
@@ -14,8 +17,13 @@ import { User } from './types';
|
||||
* @throws {Error} Throws an error if the API request fails.
|
||||
* @returns {Promise<User>} A promise that resolves to the user data.
|
||||
*/
|
||||
export const getMe = async (): Promise<User> => {
|
||||
export const getMe = async (): Promise<UserResponse> => {
|
||||
const response = await fetchAPI(`users/me/`);
|
||||
|
||||
if (response.status === 401) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new APIError(
|
||||
`Couldn't fetch user data: ${response.statusText}`,
|
||||
@@ -28,12 +36,19 @@ export const getMe = async (): Promise<User> => {
|
||||
export const KEY_AUTH = 'auth';
|
||||
|
||||
export function useAuthQuery(
|
||||
queryConfig?: UseQueryOptions<User, APIError, User>,
|
||||
queryConfig?: UseQueryOptions<UserResponse, APIError, UserResponse>,
|
||||
) {
|
||||
return useQuery<User, APIError, User>({
|
||||
return useQuery<UserResponse, APIError, UserResponse>({
|
||||
queryKey: [KEY_AUTH],
|
||||
queryFn: getMe,
|
||||
staleTime: 1000 * 60 * 15, // 15 minutes
|
||||
retry: (failureCount, error) => {
|
||||
// we assume that a 401 means the user is not logged in
|
||||
if (error.status == 401) {
|
||||
return false;
|
||||
}
|
||||
return failureCount < DEFAULT_QUERY_RETRY;
|
||||
},
|
||||
...queryConfig,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export const useSynchronizedLanguage = () => {
|
||||
);
|
||||
|
||||
const changeBackendLanguage = useCallback(
|
||||
async (language: string, user?: User) => {
|
||||
async (language: string, user?: User | null) => {
|
||||
const closestBackendLanguage = getMatchingLocales(
|
||||
availableBackendLanguages,
|
||||
[language],
|
||||
@@ -52,7 +52,7 @@ export const useSynchronizedLanguage = () => {
|
||||
);
|
||||
|
||||
const changeLanguageSynchronized = useCallback(
|
||||
async (language: string, user?: User) => {
|
||||
async (language: string, user?: User | null) => {
|
||||
if (!isSynchronizingLanguage.current) {
|
||||
isSynchronizingLanguage.current = true;
|
||||
await changeFrontendLanguage(language);
|
||||
|
||||
Reference in New Issue
Block a user