MR.HABITH
Fix: React Runtime Error #31 - Safely parse complex API error objects
b406918
import axios from 'axios';
import { logger } from '../utils/logger';
// API base URL (e.g. https://habith-smart-traffic.hf.space)
// The backend explicitly defines /api/detect for detection and /health for health check
const rawBase = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000';
const API_BASE_URL = rawBase.replace(/\/api\/?$/, ''); // Keep base at root level
const TIMEOUT = parseInt(import.meta.env.VITE_API_TIMEOUT || '90000');
logger.info(`API Configuration: ${API_BASE_URL} (Timeout: ${TIMEOUT}ms)`);
// Create Axios instance
const apiClient = axios.create({
baseURL: API_BASE_URL,
timeout: TIMEOUT,
headers: { 'Content-Type': 'application/json' },
});
// Request Interceptor
apiClient.interceptors.request.use(
(config) => {
logger.debug(`[API] ${config.method.toUpperCase()} ${config.url}`);
config.metadata = { startTime: Date.now() };
return config;
},
(error) => {
logger.error('Request Error:', error);
return Promise.reject(error);
}
);
// Response Interceptor
apiClient.interceptors.response.use(
(response) => {
const duration = Date.now() - response.config.metadata?.startTime;
logger.debug(`[API Response] ${response.status} (${duration}ms)`, response.data);
return response;
},
(error) => {
logger.error(`API Response Error: ${error.response?.status || 'Unknown'}`, error);
return Promise.reject(error);
}
);
/**
* Detect vehicles in uploaded images
* @param {FormData} formData - Form data with image files (roads parameter)
*/
export const detectVehicles = async (formData) => {
try {
logger.group('Vehicle Detection');
logger.time('Detection Request');
// The Hugging Face backend uses @app.post("/api/detect")
const response = await apiClient.post('/api/detect', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
logger.timeEnd('Detection Request');
logger.info(`Detection Success: ${response.data.total_vehicles} vehicles detected`);
logger.groupEnd();
return response.data;
} catch (error) {
logger.error('Vehicle Detection Failed:', error);
// Safely extract error message
let errorMessage = 'Detection failed. Please check the backend is running.';
if (error.response?.data?.detail) {
const detail = error.response.data.detail;
if (typeof detail === 'string') {
errorMessage = detail;
} else if (Array.isArray(detail)) {
// Handle Pydantic validation errors: {type, loc, msg, input, url}
errorMessage = detail.map(d => `${d.loc?.join('.') || 'Error'}: ${d.msg || 'Invalid input'}`).join('; ');
} else if (typeof detail === 'object') {
errorMessage = JSON.stringify(detail);
}
} else if (error.message) {
errorMessage = error.message;
}
throw { message: errorMessage, status: error.response?.status, originalError: error };
}
};
/**
* Health check
*/
export const checkHealth = async () => {
try {
logger.debug('Checking backend health...');
// The backend uses @app.get("/health")
const response = await apiClient.get('/health');
logger.info('Backend is healthy', response.data);
return response.data;
} catch (error) {
logger.warn('Backend health check failed:', error.message);
throw {
message: 'Backend server is not responding',
status: error.response?.status,
originalError: error,
};
}
};
export default apiClient;