Guide: Adding a New Country
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
Adding a new country to the WhereIsLifeCheaper platform involves a multi-step process that integrates data across the database, exchange rate services, and web scraping infrastructure. The project currently supports a variety of countries including Turkey (TR), Spain (ES), Montenegro (ME), Ukraine (UA), Kazakhstan (KZ), Uzbekistan (UZ), Germany (DE), Malaysia (MY), Albania (AL), and Austria (AT).
This guide outlines the technical requirements for registering a new country entity, configuring its local currency for comparison, and implementing the necessary scraper services to populate the system with grocery price data.
Sources: README.md:5-15, CLAUDE.md:16-17, plan.md:144-159
1. Database Configuration
The first step in adding a new country is defining its existence in the relational schema. This involves adding an entry to the countries table and subsequently configuring at least one supermarket associated with that country.
Defining the Country
The countries table stores core metadata required for localization and currency formatting. Each entry requires an ISO 3166-1 alpha-2 country code and an ISO 4217 currency code.
| Field | Type | Description |
|---|---|---|
| name | VARCHAR(100) | Full name of the country (e.g., "Germany") |
| code | VARCHAR(2) | Two-letter ISO code (e.g., "DE") |
| currency_code | VARCHAR(3) | Three-letter currency identifier (e.g., "EUR") |
| flag_emoji | VARCHAR(10) | Visual representation for the UI |
Sources: src/database/migrations/001_create_countries.sql:4-12, plan.md:73-75
Adding Supermarkets
Once a country is registered, you must define the supermarkets to be scraped. This is handled in the supermarkets table, which links back to the countries.id.
INSERT INTO supermarkets (country_id, name, website_url, scraper_class, scraper_config)
VALUES (country_id_here, 'Supermarket Name', 'https://...', 'ScraperClassName', '{}');
Sources: src/database/migrations/002_create_supermarkets.sql:6-17, plan.md:77-78
2. Currency and Exchange Rates
For cross-country comparison to function, the new country's currency must be integrated into the exchange rate synchronization system. All prices are ultimately normalized to EUR for internal comparison logic.
Rate Synchronization
The system uses a synchronization script (scripts/sync-exchange-rates.ts) that fetches rates from external APIs (such as fawazahmed0/currency-api).
- Tracked Currencies: The currency code must be added to the list of tracked currencies.
- Fallback Rates: Static fallback rates are maintained in
src/constants/exchangeRates.data.jsonto ensure the system functions even if the API is unreachable.
Sources: scripts/sync-exchange-rates.ts:16-44, src/constants/exchangeRates.data.json:1-15
Data Flow for Price Conversion
The following diagram illustrates how local prices from a new country are converted for comparison.
The system fetches rates daily and stores them in the exchange_rates table to maintain a historical audit trail and perform real-time conversions.
Sources: scripts/sync-exchange-rates.ts:110-145, plan.md:120-130
3. Implementing the Scraper
Each country requires a specialized scraper class that extends the BaseScraper. This class handles the specific DOM structure and anti-bot measures of the target supermarket.
Scraper Architecture
All scrapers must implement the lifecycle methods defined in the BaseScraper abstract class.
Key Scraper Requirements
- Browser Automation: Use Playwright (Chromium) for navigating sites.
- Stealth Mode: For modern supermarkets (like REWE in Germany), use
playwright-extrawith the stealth plugin to bypass Cloudflare protection. - Postal Code/Location: Some supermarkets require setting a delivery zone (postal code) to display accurate prices.
- Unit Normalization: Scrapers must parse local weight/volume strings (e.g., "500g", "1,5 l") into normalized units like
kgorl.
Sources: src/scrapers/germany/ReweScraper.ts:25-70, src/scrapers/germany/ReweScraper.ts:474-515, plan.md:167-175
4. Summary Checklist
To successfully add a country, ensure the following steps are completed:
- Database Migration: Run a migration or seed script to add the country to the
countriestable. - Supermarket Registration: Add at least one active supermarket entry with a designated
scraper_class. - Currency Setup: Ensure the local
currency_codeis supported inscripts/sync-exchange-rates.tsand has a fallback inexchangeRates.data.json. - Scraper Development: Create a new class in
src/scrapers/{country_name}/that extendsBaseScraper. - Registration: Register the new scraper in the
ScraperFactory. - Verification: Run
npm run scraper:test -- <scraper_name>to verify data collection.
Sources: CLAUDE.md:144-150, plan.md:112-118, README.md:65-75
Conclusion
Adding a new country is a structured process that ensures consistency across the multi-country grocery comparison engine. By adhering to the BaseScraper patterns and maintaining strict database relationships between countries, supermarkets, and products, the system can provide accurate, currency-normalized cost-of-living insights to users.