diff --git a/src/routes/etf/[tickerID]/+layout.server.ts b/src/routes/etf/[tickerID]/+layout.server.ts index 3d096758..f72914cc 100644 --- a/src/routes/etf/[tickerID]/+layout.server.ts +++ b/src/routes/etf/[tickerID]/+layout.server.ts @@ -18,15 +18,26 @@ const cleanString = (input) => { }; const fetchData = async (apiURL, apiKey, endpoint, ticker) => { - const response = await fetch(`${apiURL}${endpoint}`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-API-KEY": apiKey, - }, - body: JSON.stringify({ ticker }), - }); - return response.json(); + try { + const response = await fetch(`${apiURL}${endpoint}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-API-KEY": apiKey, + }, + body: JSON.stringify({ ticker }), + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + return data; + } catch (error) { + console.error(`Error fetching ${endpoint}:`, error); + return []; + } }; const fetchWatchlist = async (pb, userId) => { @@ -46,52 +57,68 @@ export const load = async ({ params, locals }) => { const { apiURL, apiKey, pb, user } = locals; const { tickerID } = params; - const endpoints = [ - "/etf-profile", - "/etf-holdings", - "/etf-sector-weighting", - "/stock-dividend", - "/stock-quote", - "/pre-post-quote", - "/wiim", - "/one-day-price", - "/stock-news", - ]; + try { + const endpoints = [ + "/etf-profile", + "/etf-holdings", + "/etf-sector-weighting", + "/stock-dividend", + "/stock-quote", + "/pre-post-quote", + "/wiim", + "/one-day-price", + "/stock-news", + ]; - const promises = [ - ...endpoints.map((endpoint) => - fetchData(apiURL, apiKey, endpoint, tickerID), - ), - fetchWatchlist(pb, user?.id), - ]; + const promises = [ + ...endpoints.map((endpoint) => + fetchData(apiURL, apiKey, endpoint, tickerID), + ), + fetchWatchlist(pb, user?.id), + ]; - const [ - getETFProfile, - getETFHoldings, - getETFSectorWeighting, - getStockDividend, - getStockQuote, - getPrePostQuote, - getWhyPriceMoved, - getOneDayPrice, - getNews, - getUserWatchlist, - ] = await Promise.all(promises); + const [ + getETFProfile, + getETFHoldings, + getETFSectorWeighting, + getStockDividend, + getStockQuote, + getPrePostQuote, + getWhyPriceMoved, + getOneDayPrice, + getNews, + getUserWatchlist, + ] = await Promise.all(promises); - - - return { - getETFProfile, - getETFHoldings, - getETFSectorWeighting, - getStockDividend, - getStockQuote, - getPrePostQuote, - getWhyPriceMoved, - getOneDayPrice, - getNews, - getUserWatchlist, - companyName: cleanString(getETFProfile?.at(0)?.name), - getParams: params.tickerID, - }; + return { + getETFProfile: getETFProfile || [], + getETFHoldings: getETFHoldings || [], + getETFSectorWeighting: getETFSectorWeighting || [], + getStockDividend: getStockDividend || [], + getStockQuote: getStockQuote || [], + getPrePostQuote: getPrePostQuote || [], + getWhyPriceMoved: getWhyPriceMoved || [], + getOneDayPrice: getOneDayPrice || [], + getNews: getNews || [], + getUserWatchlist: getUserWatchlist || [], + companyName: cleanString(getETFProfile?.at(0)?.name), + getParams: params.tickerID, + }; + } catch (error) { + console.error('Error in load function:', error); + return { + getETFProfile: [], + getETFHoldings: [], + getETFSectorWeighting: [], + getStockDividend: [], + getStockQuote: [], + getPrePostQuote: [], + getWhyPriceMoved: [], + getOneDayPrice: [], + getNews: [], + getUserWatchlist: [], + companyName: '', + getParams: params.tickerID, + }; + } }; \ No newline at end of file diff --git a/src/routes/etf/[tickerID]/history/+page.svelte b/src/routes/etf/[tickerID]/history/+page.svelte index c6f6395e..dda62a7a 100644 --- a/src/routes/etf/[tickerID]/history/+page.svelte +++ b/src/routes/etf/[tickerID]/history/+page.svelte @@ -267,7 +267,6 @@ rawData = prepareDataset(data?.getData, timePeriod); originalData = rawData; stockList = rawData?.slice(0, 50); - console.log(rawData); isLoaded = true; } } diff --git a/src/routes/stocks/[tickerID]/+layout.server.ts b/src/routes/stocks/[tickerID]/+layout.server.ts index 6ca01d42..359d202c 100644 --- a/src/routes/stocks/[tickerID]/+layout.server.ts +++ b/src/routes/stocks/[tickerID]/+layout.server.ts @@ -17,16 +17,47 @@ const cleanString = (input) => { return input?.replace(pattern, "").trim(); }; +const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes in milliseconds +const REQUEST_TIMEOUT = 5000; // 5 seconds +const cache = new Map(); + const fetchData = async (apiURL, apiKey, endpoint, ticker) => { - const response = await fetch(`${apiURL}${endpoint}`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-API-KEY": apiKey, - }, - body: JSON.stringify({ ticker }), - }); - return response.json(); + const cacheKey = `${endpoint}-${ticker}`; + const cached = cache.get(cacheKey); + + if (cached && Date.now() - cached.timestamp < CACHE_DURATION) { + return cached.data; + } + + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT); + + try { + const response = await fetch(`${apiURL}${endpoint}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-API-KEY": apiKey, + }, + body: JSON.stringify({ ticker }), + signal: controller.signal + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + cache.set(cacheKey, { data, timestamp: Date.now() }); + return data; + } catch (error) { + if (error.name === 'AbortError') { + throw new Error(`Request timeout for ${endpoint}`); + } + throw error; + } finally { + clearTimeout(timeoutId); + } }; const fetchWatchlist = async (pb, userId) => { @@ -58,40 +89,48 @@ export const load = async ({ params, locals }) => { "/stock-news", ]; - const promises = [ - ...endpoints.map((endpoint) => - fetchData(apiURL, apiKey, endpoint, tickerID), - ), - fetchWatchlist(pb, user?.id), - ]; + if (!tickerID) { + return { error: 'Invalid ticker ID' }; + } - const [ - getStockDeck, - getAnalystRating, - getStockQuote, - getPrePostQuote, - getWhyPriceMoved, - getOneDayPrice, - getNextEarnings, - getEarningsSurprise, - getNews, - getUserWatchlist, - ] = await Promise.all(promises); + try { + const [ + getStockDeck, + getAnalystRating, + getStockQuote, + getPrePostQuote, + getWhyPriceMoved, + getOneDayPrice, + getNextEarnings, + getEarningsSurprise, + getNews, + getUserWatchlist, + ] = await Promise.all([ + ...endpoints.map((endpoint) => + fetchData(apiURL, apiKey, endpoint, tickerID).catch(error => ({ error: error.message })) + ), + fetchWatchlist(pb, user?.id).catch(() => []) + ]); + if (!getStockDeck || getStockDeck.error) { + return { error: 'Failed to fetch stock data' }; + } - - return { - getStockDeck, - getAnalystRating, - getStockQuote, - getPrePostQuote, - getWhyPriceMoved, - getOneDayPrice, - getNextEarnings, - getEarningsSurprise, - getNews, - getUserWatchlist, - companyName: cleanString(getStockDeck?.companyName), - getParams: params.tickerID, - }; + return { + getStockDeck, + getAnalystRating, + getStockQuote, + getPrePostQuote, + getWhyPriceMoved, + getOneDayPrice, + getNextEarnings, + getEarningsSurprise, + getNews, + getUserWatchlist, + companyName: cleanString(getStockDeck?.companyName), + getParams: params.tickerID, + }; + } catch (error) { + return { error: 'Failed to load stock data' }; + } }; \ No newline at end of file