import { API_BASE } from "@/consts"; import { normalizeLink } from "./utils"; import type { ApiResponse } from "@/types/api"; import toast from "react-hot-toast"; import { tryCatch } from "./tryCatch"; interface RequestProps { error?: (err: Error) => void; success?: (data: T) => void; before?: () => void; finally?: () => void; } interface PostProps extends RequestProps { data: Record; } interface PutProps extends RequestProps { data: Record; } interface GetProps extends RequestProps { params?: Record; } interface DeleteProps extends RequestProps { params?: Record; } class Requests { constructor() {} async verifyData(res: Response): Promise { // Get response data const { data, error } = await tryCatch>(res.json()); if (error) { throw new Error(`Parsing error: ${res.statusText} - ${res.status}`); } // Check if authentication is required if ("needsAuthentication" in data && data.needsAuthentication) { window.location.replace("/login"); throw new Error("Authentication is required"); } // Check if data is ok if ("success" in data && !data.success) { throw new Error(data.error); } // Another check for unexpected error if (!res.ok) { throw new Error("Unexpected API ERROR with code: " + res.status); } // Return response data return data.data; } async get(url: string, props: GetProps): Promise { // Call before props.before?.(); // Get url parameters const urlParams = props.params ? new URLSearchParams(props.params).toString() : ""; // Normalize url const finalUrl = normalizeLink(`${API_BASE}/${url}${urlParams}`); try { // Do request const res = await fetch(finalUrl, { method: "GET", headers: { "Content-Type": "application/json", }, }); // Verify data const responseData = await this.verifyData(res); // Otherwise return response data props.success?.(responseData); return responseData; } catch (error) { const err = error as Error; // Show notification, and call error callback toast.error(err.message); props.error?.(err); return null; } finally { props.finally?.(); } } async post(url: string, props: PostProps): Promise { props.before?.(); // Normalize url const finalUrl = normalizeLink(`${API_BASE}/${url}`); try { // Do request const res = await fetch(finalUrl, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(props.data), }); // Verify data const responseData = await this.verifyData(res); // Otherwise return response data props.success?.(responseData); return responseData; } catch (error) { const err = error as Error; // Show notification, and call error callback toast.error(err.message); props.error?.(err); } finally { props.finally?.(); } } async put(url: string, props: PutProps): Promise { props.before?.(); // Normalize url const finalUrl = normalizeLink(`${API_BASE}/${url}`); try { // Do request const res = await fetch(finalUrl, { method: "PUT", headers: { "Content-Type": "application/json", }, body: JSON.stringify(props.data), }); // Verify data const responseData = await this.verifyData(res); // Otherwise return response data props.success?.(responseData); return responseData; } catch (error) { const err = error as Error; // Show notification, and call error callback toast.error(err.message); props.error?.(err); } finally { props.finally?.(); } } async delete(url: string, props: DeleteProps): Promise { // Call before props.before?.(); // Get url parameters const urlParams = props.params ? new URLSearchParams(props.params).toString() : ""; // Normalize url const finalUrl = normalizeLink(`${API_BASE}/${url}${urlParams}`); try { // Do request const res = await fetch(finalUrl, { method: "DELETE", headers: { "Content-Type": "application/json", }, }); // Verify data const responseData = await this.verifyData(res); // Otherwise return response data props.success?.(responseData); return responseData; } catch (error) { const err = error as Error; // Show notification, and call error callback toast.error(err.message); props.error?.(err); } finally { props.finally?.(); } } } export default new Requests();