4.5 KiB
4.5 KiB
CoinGeckoProvider
(no submodules)
Purpose
CoinGecko sibling for @suaveplan/currency-providers. Implements the
CurrencyProvider contract from @suaveplan/currency against the
public /simple/price endpoint, with optional Pro-tier apiKey
support that attaches the x-cg-pro-api-key header to every outbound
request. The free public tier (no key, 10–30 calls/min) and the Pro
tier share the same URL shape, so upgrading from free to Pro is a
single-field config change with no other wiring impact.
Features
CoinGeckoProvider implements CurrencyProvider—name = "coingecko",supportsCrypto = true,supportsFiat = true.- Optional
apiKeyconfig (x-cg-pro-api-keyheader attachment). - Optional
baseUrlandfetchconfig (defaulthttps://api.coingecko.com/api/v3andglobalThis.fetch). - Caller
AbortSignalpropagation via the sharedhttpRequest()helper (composes with timeouts from the helper). - Proactive
UNKNOWN_CURRENCYgate — unknownoptions.targetsreject before the HTTP call is issued. - Typed
CurrencyProviderErroron every failure path:HTTP_ERROR,PARSE_ERROR,UPSTREAM_ERROR,UNKNOWN_CURRENCY.
Usage: Basic
import { CoinGeckoProvider } from "@suaveplan/currency-provider-coingecko/coingecko/coingecko";
const provider = new CoinGeckoProvider();
const result = await provider.fetchRates({ base: "USD" });
// result.rates.BTC = 0.0000149… (1 USD ≈ 0.0000149 BTC at ~$67k/BTC)
Usage: Advanced
import { CurrencyConverter } from "@suaveplan/currency";
import { CurrencyProviderRegistry } from "@suaveplan/currency-providers";
import { CoinGeckoProvider } from "@suaveplan/currency-provider-coingecko/coingecko/coingecko";
const registry = new CurrencyProviderRegistry();
registry.register(
new CoinGeckoProvider({
apiKey: process.env.COINGECKO_PRO_KEY,
baseUrl: "https://pro-api.coingecko.com/api/v3",
}),
);
const converter = new CurrencyConverter({
provider: registry.get("coingecko"),
staleDurationMs: 60_000, // 1 minute — crypto rates move fast
});
// Targets are validated against the curated COIN_IDS map BEFORE the
// HTTP call — passing an unknown target raises UNKNOWN_CURRENCY
// without making a network round trip.
const controller = new AbortController();
const rates = await registry.get("coingecko").fetchRates({
base: "EUR",
targets: ["BTC", "ETH"],
signal: controller.signal,
});
API Reference
class CoinGeckoProvider implements CurrencyProvider
constructor(config?: CoinGeckoConfig)— config is fully optional.readonly name: "coingecko"readonly supportsCrypto: truereadonly supportsFiat: truefetchRates(options?: RateFetchOptions): Promise<RateFetchResult>- Issues
GET ${baseUrl}/simple/price?ids=<curated-ids>&vs_currencies=<base.toLowerCase()>. - Forwards
options.signalto the sharedhttpRequest()helper. - Pre-validates
options.targetsagainstCOIN_IDS; an unknown entry raisesCurrencyProviderError(UNKNOWN_CURRENCY)before any network activity. - Inverts upstream "1 crypto = X fiat" prices into the converter's "1 base = X crypto" convention.
- Throws
CurrencyProviderErrorwithcode∈ {HTTP_ERROR,PARSE_ERROR,UPSTREAM_ERROR,UNKNOWN_CURRENCY} carryingproviderName === "coingecko".
- Issues
interface CoinGeckoConfig extends NoKeyConfig
readonly baseUrl?: string— overrideshttps://api.coingecko.com/api/v3.readonly fetch?: FetchLike— overridesglobalThis.fetch.readonly apiKey?: string— Pro-tier key, attached asx-cg-pro-api-keyheader when present.
Implementation Notes
- Pre-validating
options.targetsagainstCOIN_IDS(not against the upstream response) means the rejection message names the bad symbol rather than swallowing it with a missing-key in the result map. This is the difference between "yourZZZsymbol is not in our curated set" (actionable) and "your rate result has noZZZfield" (silent defect). - The shape guard (
isResponseShape) accepts any value matchingRecord<string, Record<string, number>>rather than asserting the exactbitcoin/ethereumkeys CoinGecko currently returns. The upstream may add new coins or rename old ones, and a strict assertion would treat that as a wire-format break instead of a benign superset. - Headers are constructed once at instantiation time and frozen so the
per-request
initspread is the only mutable copy — defends against accidental key leakage into a downstream debugger.