import apiClient from './index.js' import logger from './logger.js' /** * Build a plain Error whose `.message` follows the same priority chain * the views used to apply inline: * * error.response.data.detail * → error.response.data.message * → error.response.data.error * → "HTTP {status}: {statusText}" * → error.message * → fallback * * Returns a fresh Error so callers don't have to reason about the original * axios error's shape. Views read `.message`; auth callers that need * `.response.data.detail` should bypass this helper (see authService). * * @param {*} error * @param {string} [fallback='Request failed'] * @returns {Error} */ export function handleApiError(error, fallback = 'Request failed') { const data = error?.response?.data const detail = data?.detail || data?.message || data?.error if (detail) return new Error(detail) const status = error?.response?.status if (status) return new Error(`HTTP ${status}: ${error.response.statusText}`) if (error?.message) return new Error(error.message) return new Error(fallback) } /** * Thin HOF wrapper around an axios call. Logs the failure with structured * context, then throws a normalized Error. The shape services previously * hand-rolled (try/catch + console.error + extract + throw) collapses to a * single one-liner. * * await apiCall('POST', ENDPOINTS.STUDENTS.CREATE, { data: payload }, * 'Failed to create student') * * @param {string} method HTTP verb * @param {string} url * @param {{ data?: any, params?: Object, config?: Object }} [opts] * @param {string} [fallback] * @returns {Promise} response.data */ export async function apiCall(method, url, opts, fallback) { const { data, params, config } = opts || {} try { const verb = method.toLowerCase() // Axios's verb signatures differ: GET/DELETE take (url, config); POST/PATCH // take (url, data, config). Branch so each call looks like the rest of // the codebase. const response = verb === 'get' || verb === 'delete' ? await apiClient[verb](url, { params, ...config }) : await apiClient[verb](url, data, { params, ...config }) return response.data } catch (error) { logger.error(`${method} ${url} failed`, { status: error?.response?.status, detail: error?.response?.data?.detail, }) throw handleApiError(error, fallback) } }