import { queryClient } from './queryClient' const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001/api' // Exponential backoff retry helper const retryFetch = async (fn, { maxRetries = 3, baseDelay = 1000 } = {}) => { let lastError for (let attempt = 0; attempt <= maxRetries; attempt++) { try { return await fn() } catch (err) { lastError = err // Don't retry on client errors (except 429), only server errors and network failures if (err instanceof TypeError && err.message === 'Failed to fetch') { // Network failure - retry } else if (err.response && err.response.status >= 500) { // 5xx server error - retry } else if (err.response && err.response.status === 429) { // 429 Too Many Requests - check Retry-After header const retryAfter = err.response.headers.get('Retry-After') if (retryAfter) { const delay = parseInt(retryAfter, 10) * 1000 await new Promise(resolve => setTimeout(resolve, delay)) continue } } else { // Other errors (4xx except 429) - don't retry throw err } // Wait with exponential backoff before retry if (attempt < maxRetries) { const delay = baseDelay * Math.pow(2, attempt) await new Promise(resolve => setTimeout(resolve, delay)) } } } throw lastError } export const api = { get: async (endpoint) => { return await retryFetch(async () => { let response try { response = await fetch(`${API_BASE_URL}${endpoint}`) } catch (err) { if (err instanceof TypeError && err.message === 'Failed to fetch') { throw new Error('Unable to reach the server. This may be a network or CORS issue.') } throw new Error(`Network error: ${err.message}`) } if (!response.ok) { const errorData = new Error(`API error: ${response.statusText}`) errorData.response = { status: response.status, statusText: response.statusText } throw errorData } return response.json() }, { maxRetries: 3, baseDelay: 1000 }) }, post: async (endpoint, data) => { return await retryFetch(async () => { let response try { response = await fetch(`${API_BASE_URL}${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }) } catch (err) { if (err instanceof TypeError && err.message === 'Failed to fetch') { throw new Error('Unable to reach the server. This may be a network or CORS issue.') } throw new Error(`Network error: ${err.message}`) } if (!response.ok) { let errorData try { errorData = await response.json() } catch { const err = new Error(`Server error (${response.status}): ${response.statusText}`) err.response = { status: response.status, statusText: response.statusText } throw err } const error = new Error(errorData.error || `API error: ${response.statusText}`) error.response = { status: response.status } throw error } return response.json() }, { maxRetries: 3, baseDelay: 1000 }) }, } export { queryClient }