const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5000/api/v1'; export class ApiError extends Error { constructor( public status: number, message: string, public data?: any ) { super(message); this.name = 'ApiError'; } } async function handleResponse(response: Response): Promise { if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new ApiError( response.status, errorData.message || response.statusText, errorData ); } if (response.status === 204) { return {} as T; } return response.json(); } export async function apiRequest( endpoint: string, options: RequestInit = {} ): Promise { const url = `${API_URL}${endpoint}`; const headers: Record = { 'Content-Type': 'application/json', }; // Add auth token if available if (typeof window !== 'undefined') { const token = localStorage.getItem('accessToken'); if (token) { headers['Authorization'] = `Bearer ${token}`; } } // Merge with options headers if (options.headers) { Object.assign(headers, options.headers); } const config: RequestInit = { ...options, headers, }; const response = await fetch(url, config); return handleResponse(response); } export const api = { get: (endpoint: string, options?: RequestInit) => apiRequest(endpoint, { ...options, method: 'GET' }), post: (endpoint: string, data?: any, options?: RequestInit) => apiRequest(endpoint, { ...options, method: 'POST', body: JSON.stringify(data), }), put: (endpoint: string, data?: any, options?: RequestInit) => apiRequest(endpoint, { ...options, method: 'PUT', body: JSON.stringify(data), }), patch: (endpoint: string, data?: any, options?: RequestInit) => apiRequest(endpoint, { ...options, method: 'PATCH', body: JSON.stringify(data), }), delete: (endpoint: string, options?: RequestInit) => apiRequest(endpoint, { ...options, method: 'DELETE' }), };