multi-currency
Exchange rate management, currency conversion, and Frankfurter API integration. Use when working with USD/EUR/ILS conversions, exchange rates, or multi-currency dashboard displays.
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
Multi-Currency Domain Knowledge
Supported Currencies
enum Currency {
USD // US Dollar
EUR // Euro
ILS // Israeli Shekel
}
Exchange Rate Storage
model ExchangeRate {
id String @id @default(cuid())
baseCurrency Currency
targetCurrency Currency
rate Decimal @db.Decimal(12, 6) // 6 decimal precision
date DateTime
fetchedAt DateTime @default(now())
@@unique([baseCurrency, targetCurrency, date])
}
Frankfurter API Integration
Location: src/lib/currency.ts
Base URL: https://api.frankfurter.dev/v1
fetchExchangeRates
export async function fetchExchangeRates(baseCurrency: Currency): Promise<FrankfurterResponse>
Features:
- Request deduplication via
inFlightRequestsMap - Concurrent requests for same base currency return same Promise
- Automatic cleanup after request completes
getExchangeRate
export async function getExchangeRate(from: Currency, to: Currency, date?: Date): Promise<number>
Strategy:
- Same currency returns 1 (no conversion needed)
- Check cache (ExchangeRate table) for today's rate
- Fetch from API if not cached
- Upsert to cache
- Fallback to most recent cached rate if API fails
convertAmount
export async function convertAmount(
amount: number,
from: Currency,
to: Currency,
date?: Date
): Promise<number>
- Returns amount unchanged if same currency
- Rounds to 2 decimal places:
Math.round(converted * 100) / 100
Batch Loading Pattern (N+1 Prevention)
For dashboard aggregations, use batch loading:
batchLoadExchangeRates
export async function batchLoadExchangeRates(date?: Date): Promise<RateCache>
// Returns Map<string, number> for O(1) lookups
convertAmountWithCache
export function convertAmountWithCache(
amount: number,
from: Currency,
to: Currency,
cache: RateCache
): number
- No database calls (uses preloaded cache)
- Logs warning if rate missing, returns original amount
- Use in loops/aggregations to avoid N+1 queries
Cache Key Format
function rateCacheKey(from: Currency, to: Currency): string {
return `${from}:${to}` // e.g., "USD:EUR"
}
Refresh Workflow
export async function refreshExchangeRates(): Promise<
{ success: true; updatedAt: Date } | { error: { general: string[] }; updatedAt: Date }
>
- Fetches rates for ALL base currencies
- Upserts all currency pairs for today
- Called via
refreshExchangeRatesActionfrom dashboard UI
User Preference
model User {
preferredCurrency Currency @default(USD)
}
Dashboard displays amounts in user's preferred currency via conversion.
Key Files
-
src/lib/currency.ts- All currency functions -
src/app/actions/misc.ts- refreshExchangeRatesAction -
src/lib/dashboard-ux.ts- Uses batchLoadExchangeRates for aggregation
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-01-20
- Index updated
- 2026-01-26
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!