@molecule/api-fx-rates
v1.0.1
Published
Foreign exchange rates core interface for molecule.dev
Downloads
491
Maintainers
Readme
@molecule/api-fx-rates
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Provider-agnostic foreign-exchange rates interface for molecule.dev.
Defines the FxRatesProvider interface for currency conversion and daily
reference-rate lookups. Bond packages (ECB, OpenExchange, etc.) implement
this interface. Application code uses the convenience functions
(getRate, getDailyRates, convert, listSupportedCurrencies) which
delegate to the bonded provider.
Rates are normalized as plain number ratios: 1 unit of FROM = rate units of TO.
Currency codes are ISO 4217 three-letter strings (e.g. 'USD', 'EUR', 'JPY').
Amounts are integer minor units (cents) to avoid floating-point drift.
Quick Start
import { setProvider, getRate, convert } from '@molecule/api-fx-rates'
import { provider as ecb } from '@molecule/api-fx-rates-ecb'
setProvider(ecb)
const eurUsd = await getRate('EUR', 'USD')
const usdCents = await convert(10_000, 'EUR', 'USD') // 10000 EUR cents -> USD centsType
core
Installation
npm install @molecule/api-fx-rates @molecule/api-bond @molecule/api-i18nAPI
Interfaces
FxDailyRates
A daily snapshot of reference rates, all expressed against a common pivot.
interface FxDailyRates {
/**
* The pivot currency the snapshot is quoted against
* (e.g. `'EUR'` for ECB, `'USD'` for most paid feeds).
*/
pivot: CurrencyCode
/**
* Date of the daily snapshot.
*/
asOf: Date
/**
* Map from currency code to rate: `1 unit of pivot = rates[code] units of code`.
* The pivot itself is conventionally included with rate `1`.
*/
rates: Record<CurrencyCode, number>
}FxRate
A single FX rate quote: 1 unit of {@link from} = rate units of {@link to},
as observed at {@link asOf}.
interface FxRate {
/**
* Source currency (ISO 4217).
*/
from: CurrencyCode
/**
* Target currency (ISO 4217).
*/
to: CurrencyCode
/**
* Conversion ratio: `1 unit of {from} = rate units of {to}`.
*/
rate: number
/**
* Timestamp the rate was observed/published.
*/
asOf: Date
}FxRatesOptions
Options accepted by all FX-rates provider methods.
interface FxRatesOptions {
/**
* Date the rate should be observed at. Defaults to "latest"
* if omitted. Implementations that do not support historical
* lookups MAY throw if a non-latest date is requested.
*/
asOf?: Date
}FxRatesProvider
Foreign-exchange rates provider interface.
All FX-rate providers (ECB, OpenExchange, fixtures, etc.) implement this interface. The interface is deliberately minimal so providers with very different upstream APIs can satisfy it identically.
interface FxRatesProvider {
/**
* Looks up the conversion rate `1 unit of from = rate units of to`.
*
* Implementations SHOULD compute cross-rates through their pivot when
* neither side equals the pivot.
*
* @param from - Source currency (ISO 4217).
* @param to - Target currency (ISO 4217).
* @param options - Optional asOf date for historical rates.
* @returns The conversion ratio as a plain number.
*/
getRate(from: CurrencyCode, to: CurrencyCode, options?: FxRatesOptions): Promise<number>
/**
* Returns all reference rates the provider publishes for the given day,
* normalized against the provider's pivot currency.
*
* @param options - Optional asOf date for the daily snapshot.
* @returns The full daily snapshot.
*/
getDailyRates(options?: FxRatesOptions): Promise<FxDailyRates>
/**
* Converts an integer minor-unit amount (e.g. cents) from one currency
* to another, returning an integer minor-unit amount in the target.
*
* Implementations are expected to handle currencies with non-cent minor
* units (e.g. JPY has 0 decimals) consistently with the inputs.
*
* @param amountMinor - Amount in minor units of {@link from} (e.g. cents).
* @param from - Source currency (ISO 4217).
* @param to - Target currency (ISO 4217).
* @param options - Optional asOf date for historical rates.
* @returns Converted amount in minor units of {@link to}.
*/
convert(
amountMinor: number,
from: CurrencyCode,
to: CurrencyCode,
options?: FxRatesOptions,
): Promise<number>
/**
* Lists every currency the provider currently supports.
*
* @returns Array of ISO 4217 currency codes the provider can quote.
*/
listSupportedCurrencies(): Promise<CurrencyCode[]>
}Types
CurrencyCode
ISO 4217 three-letter currency code (e.g. 'USD', 'EUR', 'JPY').
Kept as a plain string alias rather than a string-literal union so
providers can support whatever set of currencies they expose. Use
{@link FxRatesProvider.listSupportedCurrencies} to discover what a given
provider supports at runtime.
type CurrencyCode = stringFunctions
convert(amountMinor, from, to, options)
Converts an integer minor-unit amount (e.g. cents) from one currency to another using the bonded provider.
function convert(
amountMinor: number,
from: string,
to: string,
options?: FxRatesOptions,
): Promise<number>amountMinor— Amount in minor units of {@link from} (e.g. cents).from— Source currency (ISO 4217).to— Target currency (ISO 4217).options— Optional asOf date for historical rates.
Returns: Converted amount in minor units of {@link to}.
getDailyRates(options)
Returns all reference rates the bonded provider publishes for the given day, normalized against the provider's pivot currency.
function getDailyRates(options?: FxRatesOptions): Promise<FxDailyRates>options— Optional asOf date for the daily snapshot.
Returns: The full daily snapshot.
getProvider()
Retrieves the bonded FX-rates provider, throwing if none is configured.
function getProvider(): FxRatesProviderReturns: The bonded FX-rates provider.
getRate(from, to, options)
Looks up the conversion rate 1 unit of from = rate units of to.
function getRate(from: string, to: string, options?: FxRatesOptions): Promise<number>from— Source currency (ISO 4217).to— Target currency (ISO 4217).options— Optional asOf date for historical rates.
Returns: The conversion ratio as a plain number.
hasProvider()
Checks whether an FX-rates provider is currently bonded.
function hasProvider(): booleanReturns: true if an FX-rates provider is bonded.
listSupportedCurrencies()
Lists every currency the bonded provider currently supports.
function listSupportedCurrencies(): Promise<string[]>Returns: Array of ISO 4217 currency codes.
setProvider(provider)
Registers an FX-rates provider as the active singleton. Called by bond
packages (e.g. @molecule/api-fx-rates-ecb) during application startup.
function setProvider(provider: FxRatesProvider): voidprovider— The FX-rates provider implementation to bond.
Available Providers
| Provider | Package |
| ----------------- | ------------------------------------- |
| ECB FX Rates | @molecule/api-fx-rates-ecb |
| OpenExchangeRates | @molecule/api-fx-rates-openexchange |
Injection Notes
Requirements
Peer dependencies:
@molecule/api-bond^1.0.1@molecule/api-i18n^1.0.1
Runtime Dependencies
@molecule/api-bond@molecule/api-i18n
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual currency/pricing screens, and check every box off one by one. This is money: a wrong rate silently corrupts every price, so verify the NUMBERS the app computes, not just that a value rendered. A box you can't check is an integration bug to fix — not a skip:
- [ ] A known pair returns a PLAUSIBLE rate:
getRate('USD', 'EUR')yields a real ratio (roughly 0.8-1.0 for USD->EUR), never 0, null, NaN, negative, or an absurd value like 1e9 — and the UI shows it as an actual number. - [ ]
convertdoes the CORRECT MATH: converting 100 USD (amountMinor10_000cents) USD->EUR returns approximately10_000 * ratein the target's minor units, rounded sensibly for that currency (integer cents; JPY has 0 decimals), and the UI shows that converted amount — not the untouched original. - [ ] Round-trip consistency: the inverse pair is reciprocal —
getRate('EUR', 'USD')is approximately1 / getRate('USD', 'EUR')— and same-currency is identity:getRate('USD', 'USD')is exactly1andconvert(x, 'USD', 'USD')returnsxunchanged. - [ ] Changing the selected pair changes the displayed result (USD->EUR vs USD->JPY give visibly different converted amounts) — the screen is not pinned to one hardcoded rate.
- [ ] An unknown/unsupported code (e.g.
'ZZZ', absent fromlistSupportedCurrencies()) surfaces a clear error in the UI — NEVER a silent rate of 0 (which zeroes the price) or a pass-through of 1. - [ ] If a historical lookup is exposed (
options.asOf), a past date returns that day's rate (different from latest for a volatile pair), not today's. - [ ] A provider/network failure surfaces gracefully (a visible error or retry) and the amount is left unconverted — the app NEVER falls back to converting at rate 0 or 1, which would corrupt the price shown or charged.
- [ ] The provider API key stays server-side — fx-rates is server-only; the key never appears in the browser bundle, the network tab, or any client response.
