Currency Conversion System
Relevant source files
The following files were used as context for generating this wiki page:
The Currency Conversion System is a core infrastructure component of the WhereIsLifeCheaper project, designed to facilitate accurate price comparisons across multiple countries with different currencies. It provides a standardized mechanism for fetching, storing, and applying exchange rates—primarily normalized to the Euro (EUR)—to enable cross-border cost-of-living analysis.
The system spans the entire technology stack, including a background synchronization script, a PostgreSQL database for persistence, and utility functions in both the backend scrapers and the React frontend. This ensures that whether data is being scraped from a supermarket or displayed to a user, currency normalization remains consistent.
Sources: scripts/sync-exchange-rates.ts, frontend/src/utils/currency.ts, plan.md:14-25
System Architecture and Data Flow
The system operates through a multi-stage lifecycle: periodic synchronization from external APIs, storage in a centralized database, and consumption by frontend components and backend normalization services.
Synchronization Flow
Exchange rates are synchronized daily from external providers (fawazahmed0/currency-api) with fallbacks to locally stored JSON data. The synchronization script performs validation, such as checking for large rate fluctuations, before persisting data to the database.
The diagram shows the lifecycle of exchange rate data from external acquisition to frontend consumption.
Sources: scripts/sync-exchange-rates.ts:35-132, src/database/migrations/011_create_exchange_rates.sql
Data Persistence and Schema
The system uses a dedicated table and view in PostgreSQL to maintain a history of exchange rates and provide quick access to the most recent values.
Exchange Rates Schema
| Field | Type | Description |
|---|---|---|
id | SERIAL | Primary Key |
currency_code | VARCHAR(3) | ISO currency code (e.g., USD, TRY, UZS) |
rate_to_eur | DECIMAL(20, 10) | Value of 1 unit of currency in EUR |
source | VARCHAR(50) | Source of the rate (e.g., 'currency-api', 'fallback') |
fetched_at | TIMESTAMP | Time the rate was retrieved |
Sources: src/database/migrations/011_create_exchange_rates.sql:1-12
latest_exchange_rates View
To optimize performance, the system utilizes a SQL view that identifies the most recent entry for each unique currency code using DISTINCT ON (currency_code).
Sources: src/database/migrations/011_create_exchange_rates.sql:19-25
Conversion Logic and Utilities
Conversion logic is implemented in two primary locations: backend scrapers for normalizing scraped data and frontend utilities for real-time UI updates.
Backend Normalization
During the scraping process, local prices are converted to a "target" currency (usually the local currency of the country being scraped) or EUR. The buildLocalConverter function creates a closure for efficient mass conversion.
export function buildLocalConverter(
ratesToEur: Map<string, number>,
targetCurrency: string,
): (amount: number, currency: Currency) => number {
const targetRate = ratesToEur.get(targetCurrency);
return (amount: number, currency: Currency): number => {
const rate = ratesToEur.get(currency);
return amount * (rate / targetRate);
};
}
Sources: src/scrapers/rent/normalize.ts:77-92
Frontend Utilities
The frontend maintains a local state of exchangeRates initialized with fallback data and updated via the /api/rates endpoint.
convertToEUR(price, currency): Multiplies the local price by the stored rate to get the Euro equivalent.findCheapestCountry(pricesByCountry, ...): Iterates through multiple country offerings, normalizes all to EUR, and identifies the lowest price.
Sources: frontend/src/utils/currency.ts:47-50, frontend/src/utils/currency.ts:86-118
Frontend Presentation
The CurrencyRatesTable component provides a user interface for monitoring current rates. It includes features like:
- Inverse Rates: Toggling between "Currency to EUR" and "EUR to Currency".
- Visual Metadata: Displaying flags, currency symbols, and names (e.g., ₺ for TRY, ₴ for UAH).
- Source Transparency: Indicating if rates came from the API, Database, or Fallback.
The sequence shows how the frontend retrieves the latest currency data for display.
Sources: frontend/src/components/comparison/CurrencyRatesTable.tsx:238-300, frontend/src/utils/currency.ts:21-34
Summary of Tracked Currencies
The system explicitly tracks several key currencies used across the supported countries.
| Currency | Symbol | Primary Country Context |
|---|---|---|
| EUR | € | Germany, Austria, Montenegro (Official) |
| TRY | ₺ | Turkey |
| UZS | so'm | Uzbekistan |
| UAH | ₴ | Ukraine |
| KZT | ₸ | Kazakhstan |
| MYR | RM | Malaysia |
| ALL | L | Albania |
| USD | $ | International Standard / Backup |
Sources: frontend/src/utils/currency.ts:37-45, frontend/src/components/comparison/CurrencyRatesTable.tsx:10-19
The Currency Conversion System ensures that the cost-of-living data collected from disparate regions is mathematically comparable, allowing the platform to fulfill its primary goal of identifying where life is cheaper. By combining automated daily updates with robust fallback mechanisms, the system maintains high data availability and accuracy.