diff --git a/README.md b/README.md index 0efbaa2..46c78cf 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,68 @@ Applikasjonen skal: - vise tydelig lasting og feilmelding ved API-feil - fungere på både mobil og desktop -Værdata hentes direkte når applikasjonen brukes og mellomlagres ikke. +Værdata hentes direkte når applikasjonen brukes og mellomlagres ikke +## Weather service + +`weatherService` har ansvar for å hente værdata fra Open-Meteo og konvertere API-responsen til prosjektets egen `WeatherData`-type. + +Dette gjør at React-komponentene ikke trenger å kjenne til: + +* URL-en til Open-Meteo +* feltnavnene i API-responsen +* hvordan API-kallet bygges +* hvordan HTTP-feil håndteres + +Komponentene arbeider bare med prosjektets egne TypeScript-typer. + +### Hente værdata + +```ts +const weather = await getWeather(city); +``` + +Her skjer følgende: + +1. `city` inneholder navn, breddegrad og lengdegrad. +2. `getWeather(city)` sender et `fetch`-kall til Open-Meteo. +3. `await` venter til API-kallet er ferdig. +4. Relevante felter fra API-responsen mappes til prosjektets egen WeatherData-type. +5. Resultatet lagres i variabelen `weather`. + +Variabelen kan deretter brukes slik: + +```ts +console.log(weather.temperature); +console.log(weather.windSpeed); +console.log(weather.precipitation); +``` + +### Eksempel med feilhåndtering + +```ts +try { + const weather = await getWeather(city); + console.log(weather); +} catch (error) { + console.error('Kunne ikke hente værdata:', error); +} +``` + +Hvis API-kallet mislykkes, kaster `getWeather` en feil som React-komponenten kan fange og vise som en forståelig feilmelding til brukeren. + +### Dataflyt + +```mermaid +flowchart LR + City["City"] --> Service["getWeather(city)"] + Service --> API["Open-Meteo"] + API --> Service + Service --> Data["WeatherData"] + Data --> Component["React-komponent"] +``` + +Denne oppdelingen gjør API-håndteringen samlet på ett sted og lar React-komponentene bruke en enkel og stabil `WeatherData`-modell. + ## Værdata diff --git a/web/src/services/weatherService.ts b/web/src/services/weatherService.ts new file mode 100644 index 0000000..63fbf94 --- /dev/null +++ b/web/src/services/weatherService.ts @@ -0,0 +1,60 @@ +import type { City } from '../types/city'; +import type { WeatherData } from '../types/weather'; + +interface OpenMeteoCurrent { + time: string; // Local time (ISO 8601) + temperature_2m: number; // °C + apparent_temperature: number; // °C + relative_humidity_2m: number; // % + precipitation: number; // mm + weather_code: number; // WMO weather code + wind_speed_10m: number; // m/s + is_day: number; // 0 = night, 1 = day +} + +interface OpenMeteoResponse { + current: OpenMeteoCurrent; +} + +const API_URL = 'https://api.open-meteo.com/v1/forecast'; + +export async function getWeather(city: City): Promise { + const parameters = new URLSearchParams({ + latitude: city.latitude.toString(), + longitude: city.longitude.toString(), + current: [ + 'temperature_2m', + 'apparent_temperature', + 'relative_humidity_2m', + 'precipitation', + 'weather_code', + 'wind_speed_10m', + 'is_day', + ].join(','), + wind_speed_unit: 'ms', + timezone: 'auto', + }); + + const response = await fetch(`${API_URL}?${parameters.toString()}`); + + if (!response.ok) { + throw new Error( + `Kunne ikke hente værdata for ${city.name}. HTTP ${response.status}`, + ); + } + + const apiData = (await response.json()) as OpenMeteoResponse; + const current = apiData.current; + + return { + cityId: city.id, + time: current.time, + temperature: current.temperature_2m, + apparentTemperature: current.apparent_temperature, + humidity: current.relative_humidity_2m, + precipitation: current.precipitation, + weatherCode: current.weather_code, + windSpeed: current.wind_speed_10m, + isDay: current.is_day === 1, + }; +} \ No newline at end of file