56 lines
1.2 KiB
TypeScript
56 lines
1.2 KiB
TypeScript
const BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL ?? "";
|
|
|
|
export class ApiError extends Error {
|
|
status: number;
|
|
body: string;
|
|
|
|
constructor(status: number, body: string, message?: string) {
|
|
super(message ?? body ?? `Request failed with status ${status}`);
|
|
this.name = "ApiError";
|
|
this.status = status;
|
|
this.body = body;
|
|
}
|
|
}
|
|
|
|
type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
|
|
interface RequestOptions {
|
|
method?: HttpMethod;
|
|
body?: unknown;
|
|
headers?: Record<string, string>;
|
|
signal?: AbortSignal;
|
|
}
|
|
|
|
export async function apiRequest<T = unknown>(
|
|
path: string,
|
|
options: RequestOptions = {},
|
|
): Promise<T> {
|
|
const { method = "GET", body, headers = {}, signal } = options;
|
|
|
|
const res = await fetch(`${BASE_URL}${path}`, {
|
|
method,
|
|
credentials: "include",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Accept: "application/json",
|
|
...headers,
|
|
},
|
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
signal,
|
|
});
|
|
|
|
const rawText = await res.text();
|
|
|
|
if (!res.ok) {
|
|
throw new ApiError(res.status, rawText);
|
|
}
|
|
|
|
if (!rawText) return undefined as T;
|
|
|
|
try {
|
|
return JSON.parse(rawText) as T;
|
|
} catch {
|
|
return rawText as unknown as T;
|
|
}
|
|
}
|