react-app/src/hooks/useFetch.ts
import { useSSO } from '@bcgov/citz-imb-sso-react';
import { useMemo } from 'react';
export type FetchResponse = Response & { parsedBody?: Record<string, any> | string };
export type FetchType = (url: string, params?: RequestInit) => Promise<Response>;
export interface IFetch {
get: (
url: string,
params?: Record<string, any>,
requestInit?: RequestInit,
) => Promise<FetchResponse>;
put: (url: string, body?: any) => Promise<FetchResponse>;
patch: (url: string, body?: any) => Promise<FetchResponse>;
del: (url: string, body?: any) => Promise<FetchResponse>;
post: (url: string, body?: any) => Promise<FetchResponse>;
}
/**
* useFetch - hook serving as a wrapper over the native fetch implementation of node.
* You can use this pretty similarly to a certain popular library, the baseUrl can be set to avoid typing in the root path all the time,
* the authorization header is automatically set, and the request and response bodies are automatically encoded/decoded into JSON.
*
* @param baseUrl
* @returns
*/
const useFetch = (baseUrl?: string) => {
const keycloak = useSSO();
return useMemo(() => {
const absoluteFetch = async (url: string, params?: RequestInit): Promise<FetchResponse> => {
let response: Response;
params = {
headers: {
Authorization: keycloak.getAuthorizationHeaderValue(),
'Content-Type': 'application/json',
},
...params,
};
if (params && params.body) {
params.body = JSON.stringify(params.body);
}
if (url.startsWith('/')) {
response = await fetch(baseUrl + url, params);
} else {
response = await fetch(url, params);
}
// If token has expired
if (response.status === 401) {
const currentLocation = window.location.pathname;
keycloak.login({ postLoginRedirectURL: currentLocation + window.location.search });
}
const text = await response.text();
if (text.length) {
let parsedBody: any | undefined;
try {
parsedBody = JSON.parse(text);
} catch {
parsedBody = text;
}
(response as FetchResponse).parsedBody = parsedBody;
return response;
} else {
return response;
}
};
const buildQueryParams = (params: Record<string, any>): string => {
if (!params || !Object.entries(params).length) {
return '';
}
const q = Object.entries(params)
.filter(([, v]) => v !== undefined)
.map(([k, value]) => {
return `${k}=${encodeURIComponent(value)}`;
})
.join('&');
return `?${q}`;
};
const get = (url: string, params?: Record<string, any>, requestInit?: RequestInit) => {
return absoluteFetch(url + buildQueryParams(params), {
method: 'GET',
...requestInit,
});
};
const post = (url: string, body: any) => {
return absoluteFetch(url, { method: 'POST', body: body });
};
const put = (url: string, body: any) => {
return absoluteFetch(url, { method: 'PUT', body: body });
};
const patch = (url: string, body: any) => {
return absoluteFetch(url, { method: 'PATCH', body: body });
};
const del = (url: string, body: any) => {
return absoluteFetch(url, { method: 'DELETE', body: body });
};
return {
get,
patch,
put,
post,
del,
};
}, [baseUrl, keycloak]);
};
export default useFetch;