AI Computer Institute
Expert-curated CS & AI curriculum aligned to CBSE standards. A bharath.ai initiative. About Us

Project: Weather Dashboard with API

📚 Projects & Applied⏱️ 20 min read🎓 Grade 8
✍️ AI Computer Institute Editorial Team Updated: August 2026 CBSE-aligned · Peer-reviewed · 20 min read
Content curated by subject matter experts with IIT/NIT backgrounds. All chapters are fact-checked against official CBSE/NCERT syllabi.

Open any weather app on your phone right now and it shows a number — say, "34°C, Overcast" for Delhi. That number was not typed in by a person this morning, and it is not stored permanently inside your phone. Your phone does not own a thermometer sitting outside every city in the world. So where did "34" actually come from, and how did it travel from a weather station to your screen in under a second? In this project chapter, you will answer that question by building the exact mechanism yourself: a live weather dashboard that fetches real, current data from a real weather organisation's server, using nothing but a web page and about forty lines of JavaScript.

An API Is a Waiter, Not a Kitchen Door

Imagine you walk into a restaurant. You do not walk into the kitchen, open the fridge, and take out the paneer yourself. Instead, a waiter hands you a menu, you point to something written on it in an exact, agreed format ("one Paneer Butter Masala, medium spice"), and the waiter goes into the kitchen — a place you never see — and comes back with exactly that dish, plated. You never touch the stove, the raw ingredients, or the kitchen's internal mess. You only ever interact with the menu and the waiter.

An API — Application Programming Interface — plays the role of that waiter for software. A weather organisation runs enormous computers that constantly collect readings from satellites, ground stations, and weather balloons all over the planet, and run forecasting models on top of that data. You are never going to be allowed to log into those computers directly — they are private, complicated, and constantly changing. Instead, the organisation publishes a "menu": a fixed set of web addresses you are allowed to ask, and an exact format for asking. You send a request in that format; their server sends back exactly the data you asked for, in a format you agreed on in advance. You never see their databases, their code, or their internal machinery — only the menu and the reply.

This chapter uses a real, free weather API called Open-Meteo. It requires no signup, no password, and no secret key — which makes it ideal for learning, because every line of code you write here is code you can actually run and get real numbers back from.

Building the Request: An Endpoint Plus a Question

Every API request starts with an endpoint — a fixed web address that names which "waiter" you are calling. Open-Meteo's weather endpoint is:

https://api.open-meteo.com/v1/forecast

On its own, that address is incomplete — it is like telling the waiter "food" without saying which dish. You attach details after a ? mark, called query parameters, each one written as key=value and separated by &. To ask for the current weather in Delhi, you would write:

https://api.open-meteo.com/v1/forecast?latitude=28.61&longitude=77.20&current_weather=true

Notice something deliberate here: the location is given as latitude and longitude numbers, not as the text "Delhi". This is not the API being difficult — it is being precise. India alone has dozens of towns and villages that share a name with somewhere else (there is more than one "Bilaspur", more than one "Sultanpur"). A server that has to guess which "Bilaspur" you mean is a server that will guess wrong for someone. A pair of coordinates — 28.61° North, 77.20° East — points at one exact spot on Earth with no ambiguity at all. Later in this chapter you will see how a dashboard can still let a user type a city name, by first converting that name into coordinates through a second, separate API.

What Actually Happens When You Call fetch()

In JavaScript, the built-in tool for sending this kind of request is a function called fetch(). Here is the simplest possible version:

fetch("https://api.open-meteo.com/v1/forecast?latitude=28.61&longitude=77.20&current_weather=true")
  .then(response => response.json())
  .then(data => {
    console.log(data.current_weather.temperature);
  });

A very common and very reasonable-sounding misconception is: "fetch() goes and gets the data, so whatever is inside the parentheses right after fetch() must be the answer." This is wrong, and it is worth being precise about exactly why. Sending a request over the internet and waiting for Delhi-to-server-and-back travel time takes real time — tens or hundreds of milliseconds, sometimes longer on a slow mobile connection. JavaScript does not sit around frozen waiting for that reply; it keeps running the rest of your program and hands you back a Promise — a object that essentially says "I don't have your answer yet, but I promise to let you know the moment I do." This is called asynchronous behaviour: things happen out of the strict top-to-bottom order they're written in, because network delay is unpredictable.

The first .then() only fires once the reply has actually arrived, and even then, it hands you a response object — not your data yet, just an object describing the reply (its status code, its headers). Calling response.json() is a second, separate step: it takes the raw text body of that reply and parses it into a real JavaScript object you can work with. That call is itself asynchronous too (parsing can take a moment for large replies), which is why it needs its own .then().

Most real code today writes this using async/await instead, which does the exact same thing but reads top-to-bottom like ordinary code:

async function getDelhiWeather() {
  try {
    const response = await fetch(
      "https://api.open-meteo.com/v1/forecast?latitude=28.61&longitude=77.20&current_weather=true"
    );
    const data = await response.json();
    console.log(data.current_weather.temperature);
  } catch (error) {
    console.log("Could not reach the weather server:", error);
  }
}

getDelhiWeather();

The word await means "pause this function here — but only this function, nothing else on the page freezes — until the Promise settles, then continue with the resolved value." The try/catch wraps the risky, network-dependent lines so that if the phone has no internet connection at that moment, your program prints a sensible message instead of crashing.

What Comes Back: Reading JSON as Boxes Inside Boxes

The reply body itself is text written in a format called JSON (JavaScript Object Notation) — a way of writing nested data using curly braces { } for objects, square brackets [ ] for lists, and key: value pairs inside. A trimmed version of what Open-Meteo actually sends back for the Delhi request looks like this:

{
  "latitude": 28.61,
  "longitude": 77.2,
  "current_weather": {
    "temperature": 34.2,
    "windspeed": 11.4,
    "weathercode": 3,
    "time": "2026-08-11T12:00"
  }
}

A second common misconception: students often assume that because the server "sent JSON", data is automatically a ready-to-use JavaScript object the moment the reply lands. It is not — over the network, everything is just a stream of text characters. response.json() is the step that actually reads that text and builds it into real nested objects and numbers your code can index into. Skip that step, and data is not an object at all — it's still raw, unusable text.

Once parsed, reaching the temperature means walking down through the nested boxes one level at a time: the outer object contains a key called current_weather, whose value is itself another object, which contains a key called temperature. In code, that path is written with dots, matching the nesting exactly:

data.current_weather.temperature   // 34.2

The diagram below traces this whole journey — from the request your JavaScript sends, to the reply that comes back, to the exact dotted path your code walks to pull out one single number.

How Your Dashboard Talks to a Weather Server Your JavaScript (running in the browser) fetch(url) Open-Meteo Weather API server returns JSON text ① fetch(url) — GET request ?latitude=28.61&longitude=77.20&current_weather=true ② Response body: JSON as plain text { } the whole JSON object "current_weather": { ... } temperature: 34.2 weathercode: 3 data.current_weather.temperature → 34.2 The amber path is the exact chain of dots your JavaScript writes to reach one number inside a box, inside a box.

From Number to Words: Decoding weathercode

Look again at the JSON above: "weathercode": 3. Not "Overcast" — just the number 3. This is a deliberate design choice by the API, not an accident. Sending a plain integer is far smaller than sending a sentence, and a number has no language attached to it — the same API serves apps in Hindi, Tamil, French, and Japanese, and every one of them can turn "3" into their own local word for "overcast" without the server needing to know which language the app speaks. Your job as the programmer is to build that translation table yourself, once, and reuse it. Open-Meteo publishes a fixed table of these codes (they follow a World Meteorological Organization standard), and the ones you'll meet most often look like this:

const weatherCodeMap = {
  0: "Clear sky",
  1: "Mainly clear",
  2: "Partly cloudy",
  3: "Overcast",
  45: "Fog",
  51: "Light drizzle",
  61: "Slight rain",
  63: "Moderate rain",
  65: "Heavy rain",
  80: "Rain showers",
  95: "Thunderstorm"
};

This is an object used purely as a lookup table — given a code, you get back a description in one step: weatherCodeMap[3] evaluates to "Overcast". This exact pattern — a compact numeric code from a data source, decoded into a human-readable label by a table you control — shows up constantly in real software: HTTP status codes (404, 500), traffic-light states, exam grade bands. Recognising it here is a genuinely reusable idea, not just a weather-app trick.

Building the Dashboard: The HTML Skeleton

A dashboard needs somewhere on the page to put the numbers once they arrive. Give each piece of information its own element with a unique id, so JavaScript can find and update exactly that piece without touching the rest of the page:

<div id="weatherCard">
  <h2 id="cityName">Delhi</h2>
  <p id="tempDisplay">--°C</p>
  <p id="descDisplay">Loading...</p>
  <button id="refreshBtn">Refresh</button>
</div>

Notice the starting values: "--°C" and "Loading...". The network reply is not instantaneous, so the page needs something honest to show for the brief window before the data arrives — this is a small but real detail that separates a dashboard that feels broken from one that feels responsive.

Wiring It Up: The Complete Fetch-and-Display Function

Now combine everything — the URL building, the asynchronous fetch, the JSON parsing, the code lookup, and writing into the page — into one reusable function:

async function showWeather(lat, lon, cityLabel) {
  const url = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&current_weather=true`;

  const nameBox = document.getElementById("cityName");
  const tempBox = document.getElementById("tempDisplay");
  const descBox = document.getElementById("descDisplay");

  try {
    const response = await fetch(url);

    if (!response.ok) {
      descBox.textContent = "Server error: " + response.status;
      return;
    }

    const data = await response.json();
    const temp = data.current_weather.temperature;
    const code = data.current_weather.weathercode;

    nameBox.textContent = cityLabel;
    tempBox.textContent = temp + "°C";
    descBox.textContent = weatherCodeMap[code] || "Unknown conditions";
  } catch (error) {
    descBox.textContent = "Network error — check your connection.";
  }
}

showWeather(28.61, 77.20, "Delhi");

Trace it by hand with the sample reply from earlier, where temperature is 34.2 and weathercode is 3: response.ok is true, so execution moves past the error check. temp becomes 34.2, code becomes 3. Then nameBox.textContent is set to "Delhi", tempBox.textContent is set to the string "34.2°C" (note that + here joins a number and a string, producing text, not addition), and because weatherCodeMap[3] is "Overcast", descBox.textContent becomes "Overcast". The page now reads: Delhi — 34.2°C — Overcast, and it got there from four raw bytes of JSON your server sent, decoded by a table you wrote.

An Important Trap: fetch() Does Not Reject on a 404

Here is a genuinely common bug, and worth naming explicitly because it surprises even experienced programmers the first time: many students assume that if a server responds with an error — say, a "404 Not Found" because a URL was mistyped — the catch block will automatically run. It will not. fetch()'s promise only rejects for a true network failure: no internet connection, a timeout, a broken DNS lookup. A 404 or a 500 is still, technically, a completed, successful round trip over the network — the server did answer, it just answered "no". This is exactly why the function above checks response.ok explicitly before trusting the reply. Skip that check, and a broken URL silently tries to read data.current_weather.temperature from an error page that has no such field, crashing with a confusing message far from the real cause.

Letting Users Type a City Name: Chaining Two APIs

Real dashboards do not make users memorise coordinates. Open-Meteo also publishes a separate geocoding API whose only job is to convert a place name into coordinates:

https://geocoding-api.open-meteo.com/v1/search?name=Bengaluru&count=1

This reply looks like { "results": [ { "name": "Bengaluru", "latitude": 12.97, "longitude": 77.59, "country": "India" } ] } — a list, because a name like "Springfield" or even an Indian town name can genuinely match more than one place, and the geocoder returns its best matches for you to choose from. Building a name-search box means calling this endpoint first, reading the first result out of that list, and only then calling the weather endpoint from earlier — two dependent network requests, one after another:

async function showWeatherByCityName(cityQuery) {
  const geoUrl = "https://geocoding-api.open-meteo.com/v1/search?name="
    + encodeURIComponent(cityQuery) + "&count=1";

  const geoResponse = await fetch(geoUrl);
  const geoData = await geoResponse.json();

  if (!geoData.results || geoData.results.length === 0) {
    document.getElementById("descDisplay").textContent = "City not found.";
    return;
  }

  const place = geoData.results[0];
  showWeather(place.latitude, place.longitude, place.name);
}

showWeatherByCityName("Bengaluru");

Two details deserve attention. First, encodeURIComponent() converts characters that are unsafe inside a URL — spaces, for instance — into a safe encoded form; typing a two-word city name straight into a URL without it can break the request. Second, the empty-results check (geoData.results.length === 0) matters because "city not found" is not a network failure — the request succeeds fine, it just comes back with an empty list — so this, too, would slip past a catch block if you didn't check for it directly, for the same reason a 404 does.

Check Your Understanding

  1. An API and a database are not the same thing. In one sentence, explain the difference between "asking Open-Meteo's API for the weather" and "reading a weather table directly out of a database file."
  2. In the URL https://api.open-meteo.com/v1/forecast?latitude=28.61&longitude=77.20&current_weather=true, which part is the endpoint, and which parts are query parameters?
  3. response.json() is called even though the server already said it was sending "JSON". What does this line actually do, and why is it still a necessary, separate step?
  4. Suppose the API returns "weathercode": 61 and "temperature": 24.8 for Bengaluru. Using the showWeather() function and the weatherCodeMap table above, what exact string will tempBox.textContent hold, and what exact string will descBox.textContent hold, after the function finishes running?
  5. A teammate writes fetch(url).then(data => console.log(data.current_weather)) and it prints undefined. What step did they skip, and why does the code run without crashing even though it's wrong?
  6. Why does showWeatherByCityName() check geoData.results.length === 0 instead of relying on a catch block to handle a city that doesn't exist?

Answers. (1) A database is the actual stored table of data sitting on a disk; an API is a controlled doorway a separate program exposes so you can ask for specific pieces of that data without ever touching the storage or code behind it directly. (2) The endpoint is https://api.open-meteo.com/v1/forecast; everything after the ?latitude=28.61, longitude=77.20, and current_weather=true — are the query parameters. (3) It parses the raw text body of the HTTP reply into an actual JavaScript object with real properties; the network only ever carries text, so without this step data would still be an unusable string. (4) tempBox.textContent becomes the string "24.8°C"; descBox.textContent becomes "Slight rain" (from weatherCodeMap[61]). (5) They skipped response.json() — they logged the raw response object, which has no current_weather property, so accessing it gives undefined rather than throwing an error, because JavaScript returns undefined for a missing property instead of crashing. (6) Because an empty results list is not a network failure — the request completed successfully, it just found nothing — so a catch block, which only fires on true network errors, would never run for this case.

Summary

  • An API is a fixed, published way for your program to request data from someone else's server without touching their internal code or database — like ordering through a waiter instead of walking into the kitchen.
  • A request is built from an endpoint (a fixed address) plus query parameters after a ?, written as key=value pairs joined by &.
  • Locations are sent as latitude/longitude numbers, not city names, because coordinates are unambiguous and names often are not.
  • fetch() is asynchronous: it returns a Promise immediately and the actual reply arrives later, which is why await or .then() is required to use the result.
  • The reply body is raw text in JSON format; response.json() is a separate, necessary step that parses that text into a real, nested JavaScript object.
  • Reaching a value inside nested JSON means walking the dotted path that matches the nesting exactly, e.g. data.current_weather.temperature.
  • Numeric codes like weathercode are decoded into human-readable text using a lookup object you write yourself — compact, language-independent data from the API, translated locally.
  • fetch()'s promise rejects only on true network failure — never on an HTTP error status like 404 or 500 — so checking response.ok (or an empty results list, for the geocoder) must be done explicitly.
  • A name-based search means chaining two API calls: a geocoding lookup that turns a typed city name into coordinates, followed by the weather call that uses those coordinates.

Practice Exercises

Now it is time to practice! Complete these challenges to solidify your understanding:

  • Exercise 1: Write a short program that demonstrates the core concept from this chapter. Test it with at least 3 different inputs.
  • Exercise 2: Find a real-world example where project: weather dashboard with api is used in an Indian company (like TCS, Infosys, Flipkart, or ISRO). Write a paragraph explaining the connection.
  • Exercise 3: Create a mind-map connecting project: weather dashboard with api to at least 3 other topics you have studied.
← Project: Personal Expense TrackerProject: Rule-Based Chatbot →

Found this useful? Share it!

📱 WhatsApp 🐦 Twitter 💼 LinkedIn