Weather App
Fetch real weather data from an API and display it beautifully. Learn async/await, fetch(), JSON parsing, and error handling.
What You'll Learn
- How to call an external API using the
fetch()function - How to use
async/awaitto handle asynchronous operations cleanly - How to parse and extract data from a JSON response
- How to get a free API key and use it in your project
- How to show loading states and handle errors gracefully
How It Works
APIs give you real-world data
An API is like a waiter — you tell it what you want (a city name), and it brings back data (weather info). OpenWeatherMap has a free tier that lets you make up to 1,000 requests per day.
async/await makes it readable
Fetching data takes time because we wait for a server response. Instead of confusing nested callbacks, async/await lets us write 'wait for this to finish, then do the next thing' in a clear, readable way.
Parse the JSON and update the UI
The API returns a JSON object with dozens of weather fields. We pick out temperature, humidity, wind speed, and description, then update the corresponding HTML elements with those values.
Source Code
HTML Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weather App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="app">
<h1>🌤 Weather App</h1>
<div class="search-bar">
<input type="text" id="cityInput" placeholder="Enter city name...">
<button id="searchBtn">Search</button>
</div>
<div id="weatherCard" class="weather-card hidden">
<div id="cityName" class="city-name"></div>
<div id="weatherIcon" class="weather-icon"></div>
<div id="temperature" class="temperature"></div>
<div id="description" class="description"></div>
<div class="details">
<div class="detail"><span>💧</span><strong id="humidity"></strong><span>Humidity</span></div>
<div class="detail"><span>💨</span><strong id="wind"></strong><span>Wind</span></div>
<div class="detail"><span>🌡️</span><strong id="feels"></strong><span>Feels Like</span></div>
</div>
</div>
<div id="errorMsg" class="error hidden"></div>
<div id="loading" class="loading hidden">Loading...</div>
<p class="api-note">⚠️ Get your free API key at openweathermap.org and add it to script.js</p>
</div>
<script src="script.js"></script>
</body>
</html>
CSS Styling
* { margin:0; padding:0; box-sizing:border-box; }
body { min-height:100vh; background:linear-gradient(135deg,#4a1080,#8e44ad);
display:flex; align-items:center; justify-content:center;
font-family:'Segoe UI',sans-serif; padding:20px; }
.app { width:100%; max-width:400px; text-align:center; }
h1 { color:white; font-size:2rem; font-weight:800; margin-bottom:24px; }
.search-bar { display:flex; gap:10px; margin-bottom:20px; }
#cityInput { flex:1; padding:14px 18px; border:none; border-radius:50px; font-size:1rem;
outline:none; box-shadow:0 4px 15px rgba(0,0,0,0.2); }
#searchBtn { padding:14px 22px; background:#f09a0f; color:white; border:none;
border-radius:50px; font-weight:700; cursor:pointer; }
#searchBtn:hover { background:#d4880d; }
.weather-card { background:rgba(255,255,255,0.15); backdrop-filter:blur(10px);
border:1px solid rgba(255,255,255,0.2); border-radius:24px; padding:32px; color:white; }
.city-name { font-size:1.5rem; font-weight:700; margin-bottom:8px; }
.weather-icon { font-size:5rem; margin:16px 0; }
.temperature { font-size:4rem; font-weight:800; }
.description { font-size:1.1rem; opacity:0.85; text-transform:capitalize; margin:8px 0 24px; }
.details { display:grid; grid-template-columns:repeat(3,1fr); gap:12px;
background:rgba(0,0,0,0.15); border-radius:12px; padding:16px; }
.detail { display:flex; flex-direction:column; gap:4px; align-items:center; }
.detail span:first-child { font-size:1.3rem; }
.detail strong { font-size:1rem; font-weight:700; }
.detail span:last-child { font-size:0.75rem; opacity:0.75; }
.error { background:rgba(231,76,60,0.2); border:1px solid rgba(231,76,60,0.4);
color:white; padding:14px; border-radius:12px; }
.loading { color:rgba(255,255,255,0.75); font-size:1rem; padding:14px; }
.api-note { color:rgba(255,255,255,0.5); font-size:0.8rem; margin-top:20px; }
.hidden { display:none !important; }
JavaScript Logic
// 🔑 Get your FREE API key at https://openweathermap.org/api
// Sign up (free), go to API keys tab, and paste your key below
const API_KEY = 'YOUR_API_KEY_HERE';
const icons = {
Clear: '☀️', Clouds: '☁️', Rain: '🌧️',
Drizzle: '🌦️', Thunderstorm: '⛈️', Snow: '❄️',
Mist: '🌫️', Fog: '🌫️', Haze: '🌫️'
};
async function getWeather(city) {
const loading = document.getElementById('loading');
const errorMsg = document.getElementById('errorMsg');
const card = document.getElementById('weatherCard');
loading.classList.remove('hidden');
errorMsg.classList.add('hidden');
card.classList.add('hidden');
try {
const url = `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&appid=${API_KEY}&units=metric`;
const res = await fetch(url);
if (!res.ok) throw new Error('City not found');
const data = await res.json();
document.getElementById('cityName').textContent = `${data.name}, ${data.sys.country}`;
document.getElementById('temperature').textContent = `${Math.round(data.main.temp)}°C`;
document.getElementById('description').textContent = data.weather[0].description;
document.getElementById('humidity').textContent = `${data.main.humidity}%`;
document.getElementById('wind').textContent = `${data.wind.speed} m/s`;
document.getElementById('feels').textContent = `${Math.round(data.main.feels_like)}°C`;
document.getElementById('weatherIcon').textContent = icons[data.weather[0].main] || '🌡️';
card.classList.remove('hidden');
} catch (err) {
errorMsg.textContent = err.message === 'City not found'
? '🔍 City not found. Check spelling and try again.'
: '⚠️ Could not load weather. Check your API key.';
errorMsg.classList.remove('hidden');
} finally {
loading.classList.add('hidden');
}
}
document.getElementById('searchBtn').addEventListener('click', () => {
const city = document.getElementById('cityInput').value.trim();
if (city) getWeather(city);
});
document.getElementById('cityInput').addEventListener('keypress', e => {
if (e.key === 'Enter') {
const city = e.target.value.trim();
if (city) getWeather(city);
}
});
Download Source Code
Single ready-to-run HTML file. Open it in any browser!
Download ProjectSingle HTML file · All code included