This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 4.3 kB View raw
1/** 2 * Utility functions for API interactions 3 */ 4 5/** 6 * Makes a fetch request with proper error handling, timeouts, and authentication. 7 * 8 * @param {string} url - The URL to fetch 9 * @param {Object} options - Fetch options 10 * @param {number} [timeout=10000] - Timeout in milliseconds 11 * @returns {Promise<Object>} - The JSON response or error object 12 */ 13export const fetchWithTimeout = async (url, options = {}, timeout = 10000) => { 14 // Always include credentials for session cookies 15 const fetchOptions = { 16 ...options, 17 credentials: 'include', 18 }; 19 20 // Set up abort controller for timeout 21 const controller = new AbortController(); 22 const timeoutId = setTimeout(() => controller.abort(), timeout); 23 24 try { 25 const response = await fetch(url, { 26 ...fetchOptions, 27 signal: controller.signal, 28 }); 29 30 // Clear the timeout regardless of the outcome 31 clearTimeout(timeoutId); 32 33 // Check if the response was ok (status in the range 200-299) 34 if (!response.ok) { 35 // Try to get the error message from the response body 36 let errorMessage; 37 try { 38 const errorData = await response.json(); 39 errorMessage = errorData.error || errorData.message || errorData.details; 40 } catch (e) { 41 // If we can't parse the error as JSON, use the status text 42 errorMessage = response.statusText; 43 } 44 45 // Create an error object with useful properties 46 const error = new Error(errorMessage || `HTTP error ${response.status}`); 47 error.status = response.status; 48 error.statusText = response.statusText; 49 error.url = url; 50 51 // Handle auth errors specifically 52 if (response.status === 401) { 53 error.isAuthError = true; 54 } 55 56 throw error; 57 } 58 59 // Parse the JSON response 60 const data = await response.json(); 61 return data; 62 } catch (error) { 63 // Clear the timeout if we catch an error before it fires 64 clearTimeout(timeoutId); 65 66 // Enhance error with more context 67 if (error.name === 'AbortError') { 68 error.message = `Request timed out after ${timeout}ms: ${url}`; 69 error.isTimeout = true; 70 } else if (error.message && error.message.includes('Network request failed')) { 71 error.isNetworkError = true; 72 } 73 74 // Add the URL to the error for context 75 error.url = url; 76 77 throw error; 78 } 79}; 80 81/** 82 * Fetch data with retries for more reliability 83 * 84 * @param {string} url - The URL to fetch 85 * @param {Object} options - Fetch options 86 * @param {number} [maxRetries=2] - Maximum number of retries 87 * @param {number} [timeout=10000] - Timeout in milliseconds 88 * @returns {Promise<Object>} - The JSON response or error object 89 */ 90export const fetchWithRetry = async (url, options = {}, maxRetries = 2, timeout = 10000) => { 91 let lastError; 92 93 for (let attempt = 0; attempt <= maxRetries; attempt++) { 94 try { 95 // If not the first attempt, wait increasing time before retry 96 if (attempt > 0) { 97 const backoffTime = 1000 * attempt; // 1s, 2s, 3s, etc. 98 console.log(`Retry attempt ${attempt}/${maxRetries} for ${url} after ${backoffTime}ms`); 99 await new Promise(resolve => setTimeout(resolve, backoffTime)); 100 } 101 102 return await fetchWithTimeout(url, options, timeout); 103 } catch (error) { 104 lastError = error; 105 106 // Don't retry if it's an auth error - those won't go away with retries 107 if (error.status === 401 || error.status === 403) { 108 throw error; 109 } 110 111 // Don't retry if it's a client error (4xx range) except for 408 (timeout) and 429 (rate limit) 112 if (error.status && error.status >= 400 && error.status < 500 && 113 error.status !== 408 && error.status !== 429) { 114 throw error; 115 } 116 117 // Log the error but continue if we have more retries 118 console.warn(`Fetch attempt ${attempt + 1}/${maxRetries + 1} failed for ${url}:`, error.message); 119 120 // If this was the last attempt, throw the error 121 if (attempt === maxRetries) { 122 error.message = `Failed after ${maxRetries + 1} attempts: ${error.message}`; 123 throw error; 124 } 125 } 126 } 127 128 // We shouldn't get here, but just in case 129 throw lastError || new Error(`Failed to fetch ${url} after ${maxRetries + 1} attempts`); 130};