Fetch current model-based weather for a selected city
This project uses a small city selector so the API work stays focused: build a URL, fetch JSON, validate it, handle cancellation and display units and a timestamp. Open-Meteo's current conditions are model-based data, not a guaranteed live reading from a sensor beside the user.
The public endpoint below is for a learning demo under the provider's applicable non-commercial conditions. Before using it in a monetized/commercial product, check its current terms, licence and commercial endpoint requirements. Keep any private commercial credential on the server.
HTML
City Weather Practice
Choose a city and load its weather.
Weather data: Open-Meteo
weather.js
const cities = {
jaipur: { name: 'Jaipur', latitude: 26.9124, longitude: 75.7873 },
delhi: { name: 'Delhi', latitude: 28.6139, longitude: 77.2090 },
mumbai: { name: 'Mumbai', latitude: 19.0760, longitude: 72.8777 }
};
const form = document.querySelector('#weather-form');
const select = document.querySelector('#city');
const button = document.querySelector('#load');
const status = document.querySelector('#status');
const output = document.querySelector('#weather');
let activeController = null;
let requestNumber = 0;
function addLine(text, tag = 'p') {
const element = document.createElement(tag);
element.textContent = text;
output.append(element);
}
form.addEventListener('submit', async event => {
event.preventDefault();
if (!Object.hasOwn(cities, select.value)) {
status.textContent = 'Choose a listed city.';
return;
}
const city = cities[select.value];
const number = ++requestNumber;
activeController?.abort();
const controller = new AbortController();
activeController = controller;
const timer = setTimeout(() => controller.abort(), 12000);
button.disabled = true;
output.replaceChildren();
output.setAttribute('aria-busy', 'true');
status.textContent = 'Loading weather for ' + city.name + '...';
try {
const url = new URL('https://api.open-meteo.com/v1/forecast');
url.search = new URLSearchParams({
latitude: String(city.latitude),
longitude: String(city.longitude),
current: 'temperature_2m,wind_speed_10m',
temperature_unit: 'celsius',
wind_speed_unit: 'kmh',
timeformat: 'unixtime',
timezone: 'UTC'
}).toString();
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error('Weather service returned HTTP ' + response.status);
const data = await response.json();
const current = data.current;
if (!current || !Number.isFinite(current.temperature_2m)
|| !Number.isFinite(current.wind_speed_10m)
|| !Number.isFinite(current.time)) {
throw new Error('Weather response is incomplete');
}
if (number !== requestNumber) return;
addLine(city.name, 'h2');
addLine('Temperature: ' + current.temperature_2m + ' °C');
addLine('Wind speed: ' + current.wind_speed_10m + ' km/h');
addLine('Data valid at: ' + new Date(current.time * 1000).toISOString());
addLine('Source: Open-Meteo model-based current conditions.');
status.textContent = 'Weather loaded for ' + city.name + '.';
} catch (error) {
if (number !== requestNumber) return;
status.textContent = controller.signal.aborted
? 'Request timed out. Please try again.'
: 'Could not load weather. Check your connection and try again.';
} finally {
clearTimeout(timer);
if (number === requestNumber) {
button.disabled = false;
output.setAttribute('aria-busy', 'false');
activeController = null;
}
}
});Run and inspect
Save the files in a local HTTP-served workspace. Load a city and inspect the request in the Network panel. The response should contain current.temperature_2m, current.wind_speed_10m and a Unix timestamp because timeformat=unixtime was requested. The displayed timestamp is explicitly UTC through toISOString, avoiding an ambiguous location-local time string.
Test checklist
- Load each city and verify its label, units and timestamp.
- Use offline mode and confirm a readable error with the button re-enabled.
- Throttle the connection beyond the timeout and verify recovery.
- Test an incomplete/malformed response with a local mock; do not display undefined as weather.
- Confirm old requests cannot overwrite a later request result.
- Use keyboard-only selection and submission.
Extend carefully
Add city-name search as a separate geocoding step, show several matching places and let the user choose the correct location. Cache briefly and follow rate limits rather than polling continuously. Do not call the data a safety warning system or promise exact current conditions.
Official references: forecast API documentation and usage terms.