Weather App using Fetch API & Async/Await0%
Weather App using Fetch API & Async/Await
Beginner12 min read•Updated: Jul 11, 2026
Project: Weather App using Fetch API & Async/Await
In this capstone project for the Intermediate JavaScript module, you will build a production-ready Weather Dashboard Application. This project integrates the Fetch API, async/await, DOM manipulation, loading states, error boundaries, and real-time meteorological API data.
1. Project Specifications
- 1Weather API Integration: Connect to Open-Meteo or OpenWeather API to retrieve current conditions, temperature, humidity, and wind speed.
- 2City Geocoding: Convert city names (e.g. "Tokyo", "London", "New York") into geographic coordinates via geocoding API.
- 3Async Workflow: Clean
async/awaitpipeline with centralizedtry...catch. - 4UI State Management: Loading spinners, temperature toggle (°C / °F), and descriptive weather condition cards.
2. HTML Markup (index.html)
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SkyCast: Real-Time Weather</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="weather-card">
<header class="card-header">
<h1>SkyCast Weather</h1>
<form id="search-form" class="search-form">
<input type="text" id="city-input" placeholder="Enter city name (e.g. Paris)..." required />
<button type="submit" id="btn-search">Search</button>
</form>
</header>
<div id="loader" class="loader hidden">
<div class="spinner"></div>
<p>Fetching weather data...</p>
</div>
<div id="error-box" class="error-box hidden"></div>
<main id="weather-display" class="weather-display hidden">
<div class="location-row">
<h2 id="city-name">City, Country</h2>
<span id="condition-badge" class="badge">Sunny</span>
</div>
<div class="temp-row">
<div id="temperature" class="temp-val">22°C</div>
</div>
<div class="metrics-grid">
<div class="metric">
<span class="label">Wind Speed</span>
<span id="wind-speed" class="val">12 km/h</span>
</div>
<div class="metric">
<span class="label">Relative Humidity</span>
<span id="humidity" class="val">65%</span>
</div>
</div>
</main>
</div>
<script src="app.js"></script>
</body>
</html>
3. Styling the App (style.css)
CSS
:root {
--primary: #0284c7;
--bg: #030712;
--card: #111827;
--text: #f9fafb;
--text-muted: #9ca3af;
--border: #1f2937;
--danger: #ef4444;
}
body {
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
}
.weather-card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 1.25rem;
max-width: 480px;
width: 100%;
padding: 2rem;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.7);
}
.search-form {
display: flex;
gap: 0.5rem;
margin: 1.5rem 0;
}
.search-form input {
flex: 1;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
border: 1px solid var(--border);
background: #1f2937;
color: var(--text);
font-size: 1rem;
}
.search-form button {
background: var(--primary);
color: white;
border: none;
padding: 0.75rem 1.25rem;
border-radius: 0.5rem;
font-weight: 600;
cursor: pointer;
}
.location-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.badge {
background: rgba(2, 132, 199, 0.2);
color: #38bdf8;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.85rem;
}
.temp-val {
font-size: 4rem;
font-weight: 800;
margin: 1rem 0;
color: #38bdf8;
}
.metrics-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-top: 1.5rem;
padding-top: 1.5rem;
border-top: 1px solid var(--border);
}
.metric {
background: #1f2937;
padding: 1rem;
border-radius: 0.5rem;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.metric .label { font-size: 0.8rem; color: var(--text-muted); }
.metric .val { font-size: 1.2rem; font-weight: 600; }
.error-box {
background: rgba(239, 68, 68, 0.15);
color: var(--danger);
padding: 1rem;
border-radius: 0.5rem;
margin: 1rem 0;
}
.loader {
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem 0;
color: var(--text-muted);
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid var(--border);
border-top-color: var(--primary);
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin-bottom: 0.75rem;
}
@keyframes spin { to { transform: rotate(360deg); } }
.hidden { display: none !important; }
4. Application Logic (app.js)
Visual Architecture & Process Flow
How data and code flow step-by-step
Step 1
// 1. Geocoding Service (City
Step 2
Coordinates)
Interactive Quiz
Practice Quiz
Question 1
Why is encodeURIComponent(city) used when building the geocoding API URL?
Question 2
How does the app handle sequential API calls (geocoding coordinates first, then fetching weather)?
Question 3
What is the purpose of the interpretWeatherCode() helper function?
Question 4
What happens in handleWeatherSearch if the user inputs an invalid city that doesn't exist?
Question 5
Why is setLoading(false) executed in the finally block of handleWeatherSearch?
Related Lessons
| Previous Lesson | Next Lesson |
|---|---|
| Product Filter App | None |
Practice Quiz
Test your understanding of this lesson with 5 questions. Each question has one correct answer.
PreviousPrevNext