From 7dfa11276dab803c9aa3358120ab7870c46e4d6e Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 27 May 2026 20:05:19 +0200 Subject: [PATCH] Add HTTP fetch service --- .../ghostfolio/ghostfolio.module.ts | 2 + .../data-provider/data-provider.module.ts | 2 + .../rapid-api/rapid-api.service.ts | 13 ++++--- apps/api/src/services/fetch/fetch.module.ts | 9 +++++ apps/api/src/services/fetch/fetch.service.ts | 37 +++++++++++++++++++ 5 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 apps/api/src/services/fetch/fetch.module.ts create mode 100644 apps/api/src/services/fetch/fetch.service.ts diff --git a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts index 01691bcf4..484f30ee3 100644 --- a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts +++ b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.module.ts @@ -12,6 +12,7 @@ import { GoogleSheetsService } from '@ghostfolio/api/services/data-provider/goog import { ManualService } from '@ghostfolio/api/services/data-provider/manual/manual.service'; import { RapidApiService } from '@ghostfolio/api/services/data-provider/rapid-api/rapid-api.service'; import { YahooFinanceService } from '@ghostfolio/api/services/data-provider/yahoo-finance/yahoo-finance.service'; +import { FetchModule } from '@ghostfolio/api/services/fetch/fetch.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; @@ -27,6 +28,7 @@ import { GhostfolioService } from './ghostfolio.service'; imports: [ CryptocurrencyModule, DataProviderModule, + FetchModule, MarketDataModule, PrismaModule, PropertyModule, diff --git a/apps/api/src/services/data-provider/data-provider.module.ts b/apps/api/src/services/data-provider/data-provider.module.ts index 71b54f01e..2c6e9fce1 100644 --- a/apps/api/src/services/data-provider/data-provider.module.ts +++ b/apps/api/src/services/data-provider/data-provider.module.ts @@ -10,6 +10,7 @@ import { GoogleSheetsService } from '@ghostfolio/api/services/data-provider/goog import { ManualService } from '@ghostfolio/api/services/data-provider/manual/manual.service'; import { RapidApiService } from '@ghostfolio/api/services/data-provider/rapid-api/rapid-api.service'; import { YahooFinanceService } from '@ghostfolio/api/services/data-provider/yahoo-finance/yahoo-finance.service'; +import { FetchModule } from '@ghostfolio/api/services/fetch/fetch.module'; import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; @@ -26,6 +27,7 @@ import { DataProviderService } from './data-provider.service'; ConfigurationModule, CryptocurrencyModule, DataEnhancerModule, + FetchModule, MarketDataModule, PrismaModule, PropertyModule, diff --git a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts b/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts index d6bc8d0e4..22896cccc 100644 --- a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts +++ b/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts @@ -7,6 +7,7 @@ import { GetQuotesParams, GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; +import { FetchService } from '@ghostfolio/api/services/fetch/fetch.service'; import { ghostfolioFearAndGreedIndexSymbol, ghostfolioFearAndGreedIndexSymbolStocks @@ -26,7 +27,8 @@ import { format } from 'date-fns'; @Injectable() export class RapidApiService implements DataProviderInterface { public constructor( - private readonly configurationService: ConfigurationService + private readonly configurationService: ConfigurationService, + private readonly fetchService: FetchService ) {} public canHandle() { @@ -142,9 +144,8 @@ export class RapidApiService implements DataProviderInterface { oneYearAgo: { value: number; valueText: string }; }> { try { - const { fgi } = await fetch( - `https://fear-and-greed-index.p.rapidapi.com/v1/fgi`, - { + const { fgi } = await this.fetchService + .fetch(`https://fear-and-greed-index.p.rapidapi.com/v1/fgi`, { headers: { useQueryString: 'true', 'x-rapidapi-host': 'fear-and-greed-index.p.rapidapi.com', @@ -153,8 +154,8 @@ export class RapidApiService implements DataProviderInterface { signal: AbortSignal.timeout( this.configurationService.get('REQUEST_TIMEOUT') ) - } - ).then((res) => res.json()); + }) + .then((res) => res.json()); return fgi; } catch (error) { diff --git a/apps/api/src/services/fetch/fetch.module.ts b/apps/api/src/services/fetch/fetch.module.ts new file mode 100644 index 000000000..f98f2f45c --- /dev/null +++ b/apps/api/src/services/fetch/fetch.module.ts @@ -0,0 +1,9 @@ +import { FetchService } from '@ghostfolio/api/services/fetch/fetch.service'; + +import { Module } from '@nestjs/common'; + +@Module({ + exports: [FetchService], + providers: [FetchService] +}) +export class FetchModule {} diff --git a/apps/api/src/services/fetch/fetch.service.ts b/apps/api/src/services/fetch/fetch.service.ts new file mode 100644 index 000000000..8a06d3470 --- /dev/null +++ b/apps/api/src/services/fetch/fetch.service.ts @@ -0,0 +1,37 @@ +import { Injectable, Logger } from '@nestjs/common'; + +@Injectable() +export class FetchService { + public async fetch( + input: RequestInfo | URL, + init?: RequestInit + ): Promise { + const method = ( + init?.method ?? + (input instanceof Request ? input.method : undefined) ?? + 'GET' + ).toUpperCase(); + + const url = input instanceof Request ? input.url : input.toString(); + + Logger.debug(`${method} ${url}`, 'FetchService'); + + try { + return await globalThis.fetch(input, init); + } catch (error) { + if (error instanceof Error) { + Logger.error( + `${method} ${url} failed: [${error.name}] ${error.message}`, + 'FetchService' + ); + } else { + Logger.error( + `${method} ${url} failed: ${String(error)}`, + 'FetchService' + ); + } + + throw error; + } + } +}