Start learning free

LESSON 119 OF 173 · BACKEND: PAYMENTS AND OTHER SERVICES

Prices in Another Currency

Practice

Customers abroad want prices in their own money. The shop's prices are in US dollars, and the supplier publishes exchange rates at GET /rates: {"USD": 1.0, "EUR": 0.9, "GBP": 0.8}, how many euros or pounds one dollar is worth. A product of 12 dollars costs 12 × 0.9 = 10.8 euros.

The client picks a currency in the query string, the part of the URL after the ?. In the route, a parameter string? currency gets it: "EUR" for /products?currency=EUR, and null when the URL has no currency.

Rates change slowly, so asking the supplier again for every list would only make the shop slower. Ask once and keep the answer in the app's memory: a cache. A dictionary declared at the top of the file lives as long as the app runs. Here the same idea remembers customer names, so each one is read from the database only once:

var NAMES = new Dictionary<int, string>();

string CustomerName(int customerId) {
    if (!NAMES.ContainsKey(customerId)) {
        var cmd = new SqliteCommand("SELECT name FROM customers WHERE id = $id", db);
        cmd.Parameters.AddWithValue("$id", customerId);
        NAMES[customerId] = (string)cmd.ExecuteScalar();
    }
    return NAMES[customerId];
}

Your code must answer these requests, with every price rounded to 2 decimals (in C#, 12 * 0.8 is 9.600000000000001, and Math.Round(…, 2) makes it 9.6):

GET /products                →  200  [{"id": 1, "name": "Mug", "price": 12, …}, …]
GET /products?currency=EUR   →  200  [{"id": 1, "name": "Mug", "price": 10.8, …}, …]
GET /products?currency=GBP   →  200  [{"id": 1, "name": "Mug", "price": 9.6, …}, …]
GET /products?currency=JPY   →  400  {"error": "Unknown currency"}

The plain list never calls the supplier, and the three currency requests together call its /rates only once.

Your task: add a CACHE dictionary and an async GetRates helper that asks the supplier only the first time. Then make GET /products convert every price when a currency is given, and answer 400 for a currency the supplier has no rate for.


Assignment
Follow the Coding Tutorial to add the cache, the GetRates helper and the currency conversion in the /products route.


Hint
Call the supplier only inside if (!CACHE.ContainsKey("rates")). Check the currency with TryGetProperty before you use its rate: GetProperty on a missing name throws, and the request ends with a 500.


STEP 1 OF 11
    Ctrl + Enter to check