diff --git a/.env.example b/.env.example index 590702c..cf7cfc7 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,12 @@ -# No API keys are required for the default setup. +# Optional: CoinGecko API key (server-side only). +# Preferred: +# COINGECKO_PRO_API_KEY= +# or +# COINGECKO_DEMO_API_KEY= +# +# Fallback generic option: +# COINGECKO_API_KEY= +# COINGECKO_API_KEY_TYPE=demo # demo | pro +# # Optional: override the internal API route base when deploying behind a proxy. # NEXT_PUBLIC_API_BASE_URL= diff --git a/README.md b/README.md index b8a821c..6520aa3 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,22 @@ npm run sync:crypto-icons ## Environment Variables -No API keys are required. +CoinGecko key is optional but recommended to reduce rate-limit issues. + +Preferred server-side variables: + +```env +COINGECKO_PRO_API_KEY= +# or +COINGECKO_DEMO_API_KEY= +``` + +Fallback generic option: + +```env +COINGECKO_API_KEY= +COINGECKO_API_KEY_TYPE=demo # demo | pro +``` Optional variable (only if you want to call API routes through a custom base URL): diff --git a/components/converter/converter-card.tsx b/components/converter/converter-card.tsx index 54c6a32..b5f0767 100644 --- a/components/converter/converter-card.tsx +++ b/components/converter/converter-card.tsx @@ -222,7 +222,25 @@ export function ConverterCard({ variant="outline" className="border-border/70 bg-background/50" > - Rates: {data.sources.fiat} • {data.sources.crypto} + Fiat rates by + + {data.sources.fiat} + + + Price data by + + {data.sources.crypto} + diff --git a/lib/api/crypto.ts b/lib/api/crypto.ts index 235fffa..d3bd09a 100644 --- a/lib/api/crypto.ts +++ b/lib/api/crypto.ts @@ -7,6 +7,38 @@ export interface CryptoRateResult { const COINGECKO_BASE_URL = "https://api.coingecko.com/api/v3"; +function buildCoinGeckoHeaders(): HeadersInit { + const headers: Record = { + accept: "application/json" + }; + + const proApiKey = process.env.COINGECKO_PRO_API_KEY?.trim(); + const demoApiKey = process.env.COINGECKO_DEMO_API_KEY?.trim(); + const genericApiKey = process.env.COINGECKO_API_KEY?.trim(); + const genericApiKeyType = + process.env.COINGECKO_API_KEY_TYPE?.trim().toLowerCase() ?? "demo"; + + if (proApiKey) { + headers["x-cg-pro-api-key"] = proApiKey; + return headers; + } + + if (demoApiKey) { + headers["x-cg-demo-api-key"] = demoApiKey; + return headers; + } + + if (genericApiKey) { + if (genericApiKeyType === "pro") { + headers["x-cg-pro-api-key"] = genericApiKey; + } else { + headers["x-cg-demo-api-key"] = genericApiKey; + } + } + + return headers; +} + export async function fetchCryptoData(): Promise { const ids = CRYPTO_ASSETS.map((asset) => asset.providerId).filter( (id): id is string => Boolean(id) @@ -18,9 +50,7 @@ export async function fetchCryptoData(): Promise { const response = await fetch(url, { next: { revalidate: 60 }, - headers: { - accept: "application/json" - } + headers: buildCoinGeckoHeaders() }); if (!response.ok) {