Compare commits

...

2 Commits

Author SHA1 Message Date
abc35ad0ca Merge dev: Add API improvements (timeout, error handling, logging)
Some checks are pending
CI/CD / lint-and-build (push) Waiting to run
2025-11-02 17:19:41 +03:00
766d758be1 feat: Add timeout, improved error handling and logging to API client
Some checks are pending
CI/CD / lint-and-build (push) Waiting to run
2025-11-02 17:19:39 +03:00
2 changed files with 43 additions and 5 deletions

View File

@ -35,7 +35,7 @@ router.post(
.isNumeric() .isNumeric()
.withMessage('Value must be a number'), .withMessage('Value must be a number'),
], ],
validate, // Middleware для обработки ошибок валидации validate, // Проверяем результаты валидации
(req, res) => { (req, res) => {
try { try {
const { name, value } = req.body; const { name, value } = req.body;

View File

@ -26,32 +26,70 @@ export async function apiRequest<T = unknown>(
options: RequestInit = {} options: RequestInit = {}
): Promise<ApiResponse<T>> { ): Promise<ApiResponse<T>> {
try { try {
// Добавляем timeout через AbortController
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 секунд
console.log(`[API] Request to: ${apiUrl}/api/${endpoint}`);
const response = await fetch(`${apiUrl}/api/${endpoint}`, { const response = await fetch(`${apiUrl}/api/${endpoint}`, {
...options, ...options,
signal: controller.signal,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...options.headers, ...options.headers,
}, },
}); });
const data = await response.json(); clearTimeout(timeoutId);
// Сначала читаем ответ как текст, потом парсим JSON
const responseText = await response.text();
let data: any;
if (responseText) {
try {
data = JSON.parse(responseText);
} catch (jsonErr) {
// Если не удалось распарсить JSON, возвращаем текст как ошибку
console.error('Failed to parse JSON response:', responseText);
return {
success: false,
error: `Invalid JSON response: ${responseText.substring(0, 100)}`,
};
}
} else {
data = {};
}
// Если статус не успешный, возвращаем данные с ошибкой // Если статус не успешный, возвращаем данные с ошибкой
// (могут содержать детали валидации) // (могут содержать детали валидации)
if (!response.ok) { if (!response.ok) {
return { return {
success: false, success: false,
error: data.error || `HTTP error! status: ${response.status}`, error: data.error || data.message || `HTTP error! status: ${response.status}`,
errors: data.errors, // Ошибки валидации, если есть errors: data.errors, // Ошибки валидации, если есть
}; };
} }
return data; return data;
} catch (err) { } catch (err) {
console.error('API Error:', err); console.error('[API] Error:', err);
let errorMessage = 'Unknown error';
if (err instanceof Error) {
if (err.name === 'AbortError') {
errorMessage = 'Request timeout - сервер не отвечает';
} else if (err.message.includes('Failed to fetch')) {
errorMessage = 'Не удалось подключиться к серверу. Проверьте, что бэкенд запущен.';
} else {
errorMessage = err.message;
}
}
return { return {
success: false, success: false,
error: err instanceof Error ? err.message : 'Unknown error', error: errorMessage,
}; };
} }
} }