Created
September 21, 2025 10:43
-
-
Save ronak-lm/16bd89ecaa1a46ad24a6dc5b161182a2 to your computer and use it in GitHub Desktop.
Fetch Wrapper for Error Handling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| export type AppFetchResponse<T> = { | |
| response?: Response; | |
| data?: T; | |
| }; | |
| export const appFetch = async <T>( | |
| input: string, | |
| init: RequestInit = {}, | |
| ): Promise<AppFetchResponse<T>> => { | |
| // Get the access token from local storage | |
| // I've used Jotai here. You can replace with this Redux, Zustand or anything else. | |
| const { get } = getDefaultStore(); | |
| const authSession = get(authSessionAtom); | |
| const accessToken = authSession?.access_token; | |
| const refreshToken = authSession?.refresh_token; | |
| // Create the fetch request options (with the access token) | |
| const appInit: RequestInit = { ...init }; | |
| if (accessToken && refreshToken) { | |
| appInit.headers = { | |
| "authorization": "Bearer " + accessToken, | |
| "refresh-token": refreshToken, | |
| "accept": "application/json", | |
| "content-type": "application/json", | |
| ...init.headers, | |
| }; | |
| } | |
| // Make the fetch request | |
| const response = await fetch( | |
| env.api.baseUrl + (input.startsWith("/") ? input : "/" + input), // URL | |
| appInit, // Request options | |
| ); | |
| // Handle errors | |
| if (!response.ok) { | |
| // Validation Error (Will be handled by the component) | |
| if (response.status === 400) { | |
| const errorData = (await response.json()) as BadRequestData; | |
| throw new FetchBadRequestError(response, errorData); | |
| } | |
| // Unauthorized | |
| else if (response.status === 401) { | |
| await supabase.auth.signOut(); // Sign out from Supabase | |
| AppStorage.resetApp(); // Clear local storage | |
| queryClient.clear(); // Clear API cache (if you use TanStack query) | |
| return {}; | |
| } | |
| // Forbidden | |
| else if (response.status === 403) { | |
| toast.error(i18n.t("error.noPermission")); | |
| router.navigate(DEFAULT_USER_PATH); // Your dashboard's home page here | |
| return {}; | |
| } | |
| // Not Found | |
| else if (response.status === 404) { | |
| toast.error(i18n.t("error.notFound")); | |
| router.navigate(DEFAULT_USER_PATH); // Your dashboard's not found page here | |
| return {}; | |
| } | |
| // Other Errors (Will be handled by the React ErrorBoundary) | |
| else { | |
| throw new FetchError(response); | |
| } | |
| } | |
| // Handle success | |
| return { | |
| response: response, | |
| data: (await response.json()) as T, // The 'as T' adds typescript support here | |
| }; | |
| }; | |
| // Custom Errors | |
| export class FetchError extends Error { | |
| public response: Response; | |
| constructor(response: Response) { | |
| super("Fetch Error: " + response.status); | |
| this.response = response; | |
| } | |
| } | |
| export class FetchBadRequestError extends FetchError { | |
| public data: BadRequestData; | |
| constructor(response: Response, data: BadRequestData) { | |
| super(response); | |
| this.data = data; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment