Queue-North-Website/src/lib/api.js

96 lines
3.3 KiB
JavaScript

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 }