create-fetch-hooks
Version:
A simple and powerful custom React hook (useApi) that simplifies API requests with automatic loading and error states. Ideal for clean and maintainable data fetching in React components.
59 lines (58 loc) • 3.77 kB
JavaScript
import { refreshToken } from "./refreshToken";
import { FetchError } from "./types/FetchError";
import { mergeHeaders } from "./utils/mergeHeader";
import { removeMultipleSlashes } from "./utils/removeMultipleSlashes";
export async function get(baseApiUrl, url, options) {
var _a, _b, _c, _d, _e, _f;
let responseCode;
const cleanedUrl = removeMultipleSlashes(`${baseApiUrl}/${url}`);
const maxRetries = (_a = options === null || options === void 0 ? void 0 : options.retryCount) !== null && _a !== void 0 ? _a : ((options === null || options === void 0 ? void 0 : options.callRefreshToken) ? 1 : 0); // Default to 0 retries if not specified
const retryDelay = (_b = options === null || options === void 0 ? void 0 : options.retryDelay) !== null && _b !== void 0 ? _b : 0; // Default to 1 second delay
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(cleanedUrl, {
method: "GET",
headers: mergeHeaders(options === null || options === void 0 ? void 0 : options.headers, {
Authorization: `Bearer ${localStorage.getItem((options === null || options === void 0 ? void 0 : options.accessTokenLocalStorageKey) || "")}`,
test2: Math.floor(Math.random() * 9000).toString(),
}),
signal: options === null || options === void 0 ? void 0 : options.signal,
});
responseCode = response.status;
if (!response.ok) {
const error = new FetchError(`HTTP error! Status: ${response.status}`, response, response.status);
// If 403 and retries remain, wait and retry
const refreshTokenConfig = (_c = options === null || options === void 0 ? void 0 : options.callRefreshToken) === null || _c === void 0 ? void 0 : _c.call(options);
if ((refreshTokenConfig === null || refreshTokenConfig === void 0 ? void 0 : refreshTokenConfig.on.includes(response.status)) &&
attempt < maxRetries) {
// await new Promise((resolve) => {
await new Promise((resolve) => setTimeout(resolve, retryDelay));
await refreshToken(baseApiUrl, refreshTokenConfig.endpoint, refreshTokenConfig.body, refreshTokenConfig.saveAccessTokenFromResponse);
continue; // Retry the request
}
throw error;
}
const res = await response.json();
(_d = options === null || options === void 0 ? void 0 : options.onSuccess) === null || _d === void 0 ? void 0 : _d.call(options, res);
return; // Exit on success
}
catch (err) {
if (err instanceof Error && err.name !== "AbortError") {
const fetchErr = err instanceof FetchError ? err : new FetchError(err.message);
if (attempt === maxRetries) {
// Only call onError on the final attempt
(_e = options === null || options === void 0 ? void 0 : options.onError) === null || _e === void 0 ? void 0 : _e.call(options, fetchErr);
if (fetchErr.status !== undefined) {
responseCode = fetchErr.status || undefined;
}
}
}
}
finally {
if (attempt === maxRetries) {
// Call onResponseGot only on the final attempt
(_f = options === null || options === void 0 ? void 0 : options.onResponseGot) === null || _f === void 0 ? void 0 : _f.call(options, cleanedUrl, removeMultipleSlashes(cleanedUrl.replace(baseApiUrl, "/")).split("?")[0], responseCode);
}
}
}
}