Skip to content

Instantly share code, notes, and snippets.

@kulcsarrudolf
Created May 6, 2024 07:40
Show Gist options
  • Select an option

  • Save kulcsarrudolf/4fab358ca0ef7416d7628596750c3e29 to your computer and use it in GitHub Desktop.

Select an option

Save kulcsarrudolf/4fab358ca0ef7416d7628596750c3e29 to your computer and use it in GitHub Desktop.
import { useState } from "react";
import axios, { AxiosError } from "axios";
import config from "../config";
import moment from "moment";
import { useLoading } from "./useLoading";
import { useNavigate } from "react-router-dom";
interface ApiError {
code: number;
message: string;
type: string;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type RequestBody = any;
enum RequestMethod {
GET = "GET",
POST = "POST",
PUT = "PUT",
DELETE = "DELETE",
PATCH = "PATCH",
}
type ApiResponse<T> = {
data: T | null;
error: { code: number; message: string; type: string } | null;
statusCode: number | null;
};
const checkToken = async (
token: string | null,
refreshToken: string | null
) => {
const tokenExpirationDate =
token && JSON.parse(atob(token.split(".")[1])).exp;
const secondsUntilExpiration = moment
.unix(tokenExpirationDate)
.diff(moment(), "seconds");
if (secondsUntilExpiration < 15) {
try {
const response = await axios({
url: `${config.api.url}/auth/refresh-token`,
method: RequestMethod.POST,
data: {
refreshToken,
},
});
const responseData = response.data;
localStorage.setItem("token", responseData.accessToken);
localStorage.setItem("refreshToken", responseData.refreshToken);
} catch (e: unknown) {
localStorage.removeItem("token");
localStorage.removeItem("refreshToken");
window.location.reload();
}
}
};
const useApi = () => {
const [loading, setLoading] = useState(false);
const { setLoading: setGlobalLoading } = useLoading();
const navigate = useNavigate();
const refreshToken = localStorage.getItem("refreshToken");
const token = localStorage.getItem("token");
const request = async <T>(
url: string,
method: RequestMethod,
body?: RequestBody,
secured = true,
contentType: string = "application/json"
): Promise<ApiResponse<T>> => {
setLoading(true);
setGlobalLoading(true);
if (secured) {
await checkToken(token, refreshToken);
if (!localStorage.getItem("token")) {
navigate("/");
return {
data: null,
error: { code: 401, message: "Unauthorized", type: "error" },
statusCode: 401,
};
}
}
const headers: Record<string, string | undefined> = {
Authorization: secured
? `Bearer ${localStorage.getItem("token")}`
: undefined,
};
if (contentType !== "multipart/form-data") {
headers["Content-Type"] = contentType;
}
try {
const response = await axios({
url: `${config.api.url}${url}`,
method,
data: body,
headers,
});
return {
data: response.data,
error: null,
statusCode: response.status,
};
} catch (e: unknown) {
const errorResponse = e as AxiosError;
const errorData = errorResponse.response?.data as ApiError;
return {
data: null,
error: {
code: errorData?.code || 500,
message: errorData?.message || "Something went wrong",
type: errorData?.type || "error",
},
statusCode: errorResponse.response?.status || 500,
};
} finally {
setLoading(false);
setGlobalLoading(false);
}
};
const get = <T>(url: string, secured = true) =>
request<T>(url, RequestMethod.GET, undefined, secured);
const post = <T>(url: string, body: RequestBody, secured = true) =>
request<T>(url, RequestMethod.POST, body, secured);
const put = <T>(url: string, body: RequestBody, secured = true) =>
request<T>(url, RequestMethod.PUT, body, secured);
const patch = <T>(url: string, body: RequestBody, secured = true) =>
request<T>(url, RequestMethod.PATCH, body, secured);
const del = <T>(url: string, secured = true) =>
request<T>(url, RequestMethod.DELETE, undefined, secured);
const postFormData = <T>(url: string, formData: FormData, secured = true) =>
request<T>(
url,
RequestMethod.POST,
formData,
secured,
"multipart/form-data"
);
return { loading, get, post, put, patch, del, postFormData };
};
export default useApi;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment