Files
fotospiel-app/resources/js/guest-v2/services/apiClient.ts
2026-02-02 13:01:20 +01:00

96 lines
2.2 KiB
TypeScript

export type ApiErrorPayload = {
error?: {
code?: string;
title?: string;
message?: string;
meta?: Record<string, unknown>;
};
code?: string;
message?: string;
};
export type ApiError = Error & {
status?: number;
code?: string;
meta?: Record<string, unknown>;
};
export type FetchJsonResult<T> = {
data: T | null;
etag: string | null;
notModified: boolean;
status: number;
};
type FetchJsonOptions = {
method?: string;
headers?: HeadersInit;
body?: BodyInit | null;
signal?: AbortSignal;
etag?: string | null;
noStore?: boolean;
};
export async function fetchJson<T>(url: string, options: FetchJsonOptions = {}): Promise<FetchJsonResult<T>> {
const headers: Record<string, string> = {
Accept: 'application/json',
};
if (options.noStore) {
headers['Cache-Control'] = 'no-store';
}
if (options.etag) {
headers['If-None-Match'] = options.etag;
}
if (options.headers) {
Object.assign(headers, options.headers as Record<string, string>);
}
const response = await fetch(url, {
method: options.method ?? 'GET',
headers,
body: options.body ?? null,
signal: options.signal,
credentials: 'include',
});
if (response.status === 304) {
return {
data: null,
etag: response.headers.get('ETag') ?? options.etag ?? null,
notModified: true,
status: response.status,
};
}
if (!response.ok) {
const errorPayload = await safeParseError(response);
const error: ApiError = new Error(errorPayload?.error?.message ?? errorPayload?.message ?? `Request failed (${response.status})`);
error.status = response.status;
error.code = errorPayload?.error?.code ?? errorPayload?.code;
if (errorPayload?.error?.meta) {
error.meta = errorPayload.error.meta;
}
throw error;
}
const data = (await response.json()) as T;
return {
data,
etag: response.headers.get('ETag'),
notModified: false,
status: response.status,
};
}
async function safeParseError(response: Response): Promise<ApiErrorPayload | null> {
try {
const payload = (await response.clone().json()) as ApiErrorPayload;
return payload;
} catch (error) {
return null;
}
}