feat: add CoinGecko API key support with env-based attribution and docs updates
This commit is contained in:
11
.env.example
11
.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=
|
||||
|
||||
17
README.md
17
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):
|
||||
|
||||
|
||||
@@ -222,7 +222,25 @@ export function ConverterCard({
|
||||
variant="outline"
|
||||
className="border-border/70 bg-background/50"
|
||||
>
|
||||
Rates: {data.sources.fiat} • {data.sources.crypto}
|
||||
<span className="text-muted-foreground">Fiat rates by</span>
|
||||
<a
|
||||
href="https://frankfurter.dev/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="ml-1 text-foreground transition-colors hover:text-cyan-100"
|
||||
>
|
||||
{data.sources.fiat}
|
||||
</a>
|
||||
<span className="mx-1 text-muted-foreground">•</span>
|
||||
<span className="text-muted-foreground">Price data by</span>
|
||||
<a
|
||||
href="https://www.coingecko.com/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="ml-1 text-foreground transition-colors hover:text-cyan-100"
|
||||
>
|
||||
{data.sources.crypto}
|
||||
</a>
|
||||
</Badge>
|
||||
</div>
|
||||
<CardDescription className="pr-1 text-base/7 sm:text-sm">
|
||||
|
||||
@@ -7,6 +7,38 @@ export interface CryptoRateResult {
|
||||
|
||||
const COINGECKO_BASE_URL = "https://api.coingecko.com/api/v3";
|
||||
|
||||
function buildCoinGeckoHeaders(): HeadersInit {
|
||||
const headers: Record<string, string> = {
|
||||
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<CryptoRateResult> {
|
||||
const ids = CRYPTO_ASSETS.map((asset) => asset.providerId).filter(
|
||||
(id): id is string => Boolean(id)
|
||||
@@ -18,9 +50,7 @@ export async function fetchCryptoData(): Promise<CryptoRateResult> {
|
||||
|
||||
const response = await fetch(url, {
|
||||
next: { revalidate: 60 },
|
||||
headers: {
|
||||
accept: "application/json"
|
||||
}
|
||||
headers: buildCoinGeckoHeaders()
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
Reference in New Issue
Block a user