type FetchOptions = Omit & { params?: Record; headers?: Record; body?: any; }; export const API_URL = "/api"; const api = { async fetch(url: string, options?: Partial) { const headers: Record = {}; const _url = new URL(API_URL + url, window.location.origin); if (options?.params) { Object.entries(options.params).forEach(([key, value]) => { _url.searchParams.set(key, String(value)); }); } if ( typeof options?.body === "object" && !(options.body instanceof FormData) ) { options.body = JSON.stringify(options.body); headers["Content-Type"] = "application/json"; } const res = await fetch(_url, { ...options, headers: { ...headers, ...(options?.headers || {}) }, }); if (!res.ok) { const json = await res.json().catch(() => {}); const message = json?.message || res.statusText; throw new Error(message); } const isJson = res.headers .get("Content-Type") ?.includes("application/json"); if (isJson) { const json = (await res.json()) as T; return json; } const text = await res.text(); return text as unknown as T; }, async get(url: string, options?: Partial) { return this.fetch(url, { ...options, method: "GET", }); }, async post(url: string, options?: Partial) { return this.fetch(url, { ...options, method: "POST", }); }, async put(url: string, options?: Partial) { return this.fetch(url, { ...options, method: "PUT", }); }, async delete(url: string, options?: Partial) { return this.fetch(url, { ...options, method: "DELETE", }); }, }; export default api;