From 766d758be1f1e5bbdc2236b9e707769aaeaa0fee Mon Sep 17 00:00:00 2001 From: DosAi Date: Sun, 2 Nov 2025 17:19:39 +0300 Subject: [PATCH] feat: Add timeout, improved error handling and logging to API client --- backend/routes/example.js | 2 +- frontend/lib/api.ts | 46 +++++++++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/backend/routes/example.js b/backend/routes/example.js index f55dc17..5c51b6a 100644 --- a/backend/routes/example.js +++ b/backend/routes/example.js @@ -35,7 +35,7 @@ router.post( .isNumeric() .withMessage('Value must be a number'), ], - validate, // Middleware для обработки ошибок валидации + validate, // Проверяем результаты валидации (req, res) => { try { const { name, value } = req.body; diff --git a/frontend/lib/api.ts b/frontend/lib/api.ts index e320a4f..509ac17 100644 --- a/frontend/lib/api.ts +++ b/frontend/lib/api.ts @@ -26,32 +26,70 @@ export async function apiRequest( options: RequestInit = {} ): Promise> { 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}`, { ...options, + signal: controller.signal, headers: { 'Content-Type': 'application/json', ...options.headers, }, }); + + clearTimeout(timeoutId); - const data = await response.json(); + // Сначала читаем ответ как текст, потом парсим 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) { return { success: false, - error: data.error || `HTTP error! status: ${response.status}`, + error: data.error || data.message || `HTTP error! status: ${response.status}`, errors: data.errors, // Ошибки валидации, если есть }; } return data; } 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 { success: false, - error: err instanceof Error ? err.message : 'Unknown error', + error: errorMessage, }; } }