From cfb756d82cf25a20ff9e54040bdb3b15381de2f4 Mon Sep 17 00:00:00 2001 From: Leons Aleksandrovs Date: Sun, 13 Jul 2025 12:42:37 +0300 Subject: [PATCH] Upload requests class --- requests.ts | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 requests.ts diff --git a/requests.ts b/requests.ts new file mode 100644 index 0000000..2c19569 --- /dev/null +++ b/requests.ts @@ -0,0 +1,198 @@ +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();