Skip to content

Instantly share code, notes, and snippets.

@isocroft
Last active December 14, 2025 02:46
Show Gist options
  • Select an option

  • Save isocroft/fbb87a7f865e9f6dbff3c577f9cfb65b to your computer and use it in GitHub Desktop.

Select an option

Save isocroft/fbb87a7f865e9f6dbff3c577f9cfb65b to your computer and use it in GitHub Desktop.
Utility to help extract clear, meaningful, human-oriented error messages (as opposed to generic & cryptic error messages) from an axios error object on a web frontend application to aid debugging on the backend
import axios, { AxiosError, AxiosResponse, AxiosRequestConfig } from "axios";
const expandOnError = (errorMessage: string, errorCode?: string, errorResponse?: AxiosResponse) => {
if (errorMessage === "Network Error" && Boolean(!errorResponse)) {
/* @HINT: The message returned below simply means that a CORS error occured OR the name resolution failed */
return errorCode === undefined
? "We are currently updating our systems... Please try again later"
: "Your ISP seems to have some other network-related issues";
}
return "The browser restricted access to the server response! Please contact admin";
};
const isCORSViolation = (request: XMLHttpRequest, config?: AxiosRequestConfig) => {
const frontendURIHost = window.location.host;
const backendBaseURL = new URL(config?.baseURL || config?.url || "https://x.yz");
const backendURIHost = backendBaseURL.host;
const isCrossDomainRequest = backendURIHost !== "x.yz" && frontendURIHost !== backendURIHost;
let hasAccessControlOnOrigin = false;
const requestHasValidStatus = Boolean((request.status >= 200 && request.status <= 508));
const contentType = request.getResponseHeader("Content-Type");
if (isCrossDomainRequest) {
let allowedOrigin = "";
try {
if (request.withCredentials) {
allowedOrigin = request.getResponseHeader("Access-Control-Allow-Origin") || "";
}
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
} catch (_) { allowedOrigin = ""; }
if (allowedOrigin.trim() === "*") {
return true;
} else {
hasAccessControlOnOrigin = Boolean(allowedOrigin.trim() === backendBaseURL.origin.trim());
}
}
return ((isCrossDomainRequest && requestHasValidStatus) && (!hasAccessControlOnOrigin && contentType !== null));
};
export const getMessageFromAxiosError = (error: AxiosError) => {
let message = "";
const isNotCORSViolation = !isCORSViolation(error?.request as XMLHttpRequest, error?.config);
/* @CHECK: https://axios-http.com/docs/handling_errors */
/* @SEE: https://www.intricatecloud.io/2020/03/how-to-handle-api-errors-in-your-web-app-using-axios/ */
/* @HINT: These are the varying ranges of error(s) that can occur when making async HTTP requests */
const isServerResponseEmpty = Boolean(!error?.response) && ((Boolean(error?.request) && isNotCORSViolation) && error?.code !== "ERR_NETWORK");
const isServerTimedOut = error?.code === "ECONNABORTED" || error?.code === "ETIMEDOUT";
const isServerUnreachable = error?.code === "ECONNREFUSED";
const isClientOffline = window !== undefined ? !window.navigator.onLine : false;
const errorMessagesMap = {
unreachable: "Our systems are unreachable! Please try again later",
timeout: "Our systems request(s) timed out! Please try again",
indeterminate: "Something went wrong! Please try again",
offline: "Your internet is unstable! Please check and try again"
};
switch (true) {
case isClientOffline:
message = errorMessagesMap["offline"];
break;
case isServerUnreachable:
message = errorMessagesMap["unreachable"];
break;
case isServerTimedOut:
message = errorMessagesMap["timeout"];
break;
case isServerResponseEmpty:
message = "Our systems response returned empty! Please try again";
break;
default:
message = Boolean(error?.response)
? errorMessagesMap["indeterminate"]
: expandOnError(error.message, error?.code, error?.response);
}
return message;
};
@isocroft

isocroft commented Dec 6, 2025

Copy link
Copy Markdown
Author
export const useRouteQueryPrefetch = ({ queryOptions, prefetchOnMount = false, cacheAndPersist = false }: { queryOptions: UseQueryOptions, prefetchOnMount: boolean, cacheAndPersist: boolean }) => {
    const navigate = useNavigate();
    const queryClient = useQueryClient();
    const [isPending, startTransition] = useTransition()
    const isFirstRender = useIsFirstRender();
  
    useEffect(() => {
      if (isFirstRender && prefetchOnMount) {
        /* @HINT:  Prefetch in background just after component mount */
        if (!cacheAndPersist) {
          queryClient.prefetchQuery(queryOptions);
        } else {
          try {
            /* @CHECK: https://tanstack.com/query/latest/docs/reference/QueryClient#queryclientensurequerydata */
            queryClient.ensureQueryData(queryOptions);
            /* eslint-disable-next-line @typescript-eslint/no-unused-vars */
          } catch (_) { /* eslint-disable no-empty */ }
        }
      }
    /* eslint-disable-next-line react-hooks/exhaustive-deps */
    }, [prefetchOnMount, cacheAndPersist, isFirstRender]);
    
    const handlePathNavigation = async (urlPathname = '/', queryOptions = { queryKey: [], queryFn: () => null }) => {
      /* @HINT: Prefetch during route navigation */
      await queryClient.prefetchQuery(queryOptions);
      navigate(urlPathname);
    };
    
    const onRouteNavigationTriggered = (e: React.MouseEvent<HTMLElement> & { target: HTMLElement }) => {
      if (isPending) {
        e.stopPropagation();
        e.preventDefault();
        return;
      }
      
      e.persist();
      
      startTransition(async () => {
        const { pathname } = new URL(e.target.getAttribute('data-href') || "/", window.location.origin);
        /* @ts-ignore */
        await handlePathNavigation(pathname, queryOptions);
      });
    };
  
    return {
      isRoutePrefetching: isPending,
      onRouteNavigation: onRouteNavigationTriggered
    } as const;  
  };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment