store/resources/js/lib/api.ts

63 lines
2.0 KiB
TypeScript

function getXsrfToken(): string {
const match = document.cookie
.split('; ')
.find((row) => row.startsWith('XSRF-TOKEN='));
return match ? decodeURIComponent(match.split('=')[1] ?? '') : '';
}
export async function apiFetch<T>(url: string, options: RequestInit = {}): Promise<T> {
const isFormData = options.body instanceof FormData;
const headers: Record<string, string> = {
Accept: 'application/json',
'X-XSRF-TOKEN': getXsrfToken(),
'X-Requested-With': 'XMLHttpRequest',
...(!isFormData && { 'Content-Type': 'application/json' }),
...(options.headers as Record<string, string>),
};
// Remove any null/undefined headers
Object.keys(headers).forEach((key) => {
if (headers[key] == null) {
delete headers[key];
}
});
const response = await fetch(url, {
...options,
headers,
credentials: 'same-origin',
});
const contentType = response.headers.get('content-type') ?? '';
const isJson = contentType.includes('application/json');
if (!response.ok) {
if (isJson) {
const error = (await response.json().catch(() => ({}))) as {
message?: string;
errors?: Record<string, string[]>;
};
const firstValidationError = error.errors
? Object.values(error.errors).flat()[0]
: undefined;
throw new Error(firstValidationError ?? error.message ?? 'Permintaan gagal diproses.');
}
if (response.status === 401 || response.status === 419) {
throw new Error('Sesi Anda telah berakhir. Silakan muat ulang halaman dan coba lagi.');
}
throw new Error(`Permintaan gagal diproses (HTTP ${response.status}).`);
}
if (!isJson) {
throw new Error('Server mengembalikan respons yang tidak valid. Silakan muat ulang halaman.');
}
return response.json() as Promise<T>;
}