NextNodeTemplate/frontend/lib/api.ts
DosAi 766d758be1
Some checks are pending
CI/CD / lint-and-build (push) Waiting to run
feat: Add timeout, improved error handling and logging to API client
2025-11-02 17:19:39 +03:00

97 lines
2.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Утилиты для работы с API
const getApiUrl = (): string => {
if (typeof window === 'undefined') {
// SSR
return process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
}
const isLocalhost = window.location.hostname === 'localhost';
return isLocalhost
? 'http://localhost:3001'
: process.env.NEXT_PUBLIC_API_URL || 'https://your-api-domain.com';
};
export const apiUrl = getApiUrl();
export interface ApiResponse<T = unknown> {
success: boolean;
data?: T;
error?: string;
errors?: Array<{ msg?: string; param?: string }>; // Ошибки валидации
}
export async function apiRequest<T = unknown>(
endpoint: string,
options: RequestInit = {}
): Promise<ApiResponse<T>> {
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);
// Сначала читаем ответ как текст, потом парсим 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 || data.message || `HTTP error! status: ${response.status}`,
errors: data.errors, // Ошибки валидации, если есть
};
}
return data;
} catch (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: errorMessage,
};
}
}