import type { ApiRequestOptions, ApiError } from '../types/api.types.ts'; import { getUserId } from '../utils/index.ts'; const DEFAULT_HEADERS: HeadersInit = { 'Content-Type': 'application/json', }; const BASE_URL = process.env.REACT_APP_API_BASE_URL?.replace(/\/$/, ''); export type { ApiRequestOptions, ApiError }; export async function apiRequest({ path, method = 'GET', body, headers, signal, }: ApiRequestOptions): Promise { const url = `${BASE_URL}${path.startsWith('/') ? '' : '/'}${path}`; // Automatically include user ID header if available const userId = getUserId(); // Convert headers to plain object if needed let baseHeaders: Record = {}; if (headers instanceof Headers) { baseHeaders = Object.fromEntries(headers.entries()); } else if (headers) { baseHeaders = headers as Record; } const requestHeaders: Record = { ...(DEFAULT_HEADERS as Record), ...baseHeaders, }; if (userId) { requestHeaders['X-User-ID'] = userId; } const response = await fetch(url, { method, headers: requestHeaders, body: body ? JSON.stringify(body) : undefined, signal, }); const contentType = response.headers.get('content-type'); const isJson = contentType?.includes('application/json'); const payload = isJson ? await response.json().catch(() => undefined) : undefined; if (!response.ok) { const error: ApiError = { status: response.status, message: (payload as { message?: string })?.message || response.statusText || 'Request failed', details: payload, }; throw error; } return payload as TResponse; } export const api = { get: (path: string, init?: Omit) => apiRequest({ path, method: 'GET', ...init }), post: ( path: string, body?: TBody, init?: Omit, 'path' | 'method' | 'body'>, ) => apiRequest({ path, method: 'POST', body, ...init }), put: ( path: string, body?: TBody, init?: Omit, 'path' | 'method' | 'body'>, ) => apiRequest({ path, method: 'PUT', body, ...init }), patch: ( path: string, body?: TBody, init?: Omit, 'path' | 'method' | 'body'>, ) => apiRequest({ path, method: 'PATCH', body, ...init }), delete: (path: string, init?: Omit) => apiRequest({ path, method: 'DELETE', ...init }), }; export default api;