Back to list
avifenesh

multi-currency

by avifenesh

0🍴 0📅 Jan 25, 2026

SKILL.md


name: multi-currency description: Exchange rate management, currency conversion, and Frankfurter API integration. Use when working with USD/EUR/ILS conversions, exchange rates, or multi-currency dashboard displays.

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 inFlightRequests Map
  • 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:

  1. Same currency returns 1 (no conversion needed)
  2. Check cache (ExchangeRate table) for today's rate
  3. Fetch from API if not cached
  4. Upsert to cache
  5. 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 refreshExchangeRatesAction from 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

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon