Display 7 Day Weather Forecast with OpenWeather API

Display 7 Day Weather Forecast with OpenWeather API

Using modern JavaScript techniques

There are many weather APIs that allow us to get data and display forecast for a specific location. I found openweathermap API to be a good choice. It’s free up to 1,000,000 calls/month and there’s a One Call API that gives you all the essential information you need. I am going to use it to build a weather web ‘widget’, that displays a 7-day forecast with the following data:

  • day

  • temperature (in Celsius)

  • icon (clear sky, rain, mist, etc.)

Photo by [Raphael Rychetsky](https://cdn.hashnode.com/res/hashnode/image/upload/v1621430475006/ewTxoPbb7.html) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)Photo by Raphael Rychetsky on Unsplash

We can split the process into 3 sections:

  1. Get the data

  2. Display the data

  3. Performance improvement — load the resources when the user scrolls towards the component (Intersection Observer API)

design of weather componentdesign of weather component

Note: Code examples below are written in vanilla JavaScript so implementation may differ and it depends on your tech stack. I am not going to share CSS and you can style it as you like.

1. Get the data

The first thing we have to do is register on openweathermap in order to create an API key. After that we can follow the One Call API documentation. The endpoint looks like this:

https://api.openweathermap.org/data/2.5/onecall?lat={lat}&lon={lon}&exclude={part}&appid={API key}

We can get the latitude and the longitude from Google maps for example. Right click on the map and copy the coordinates. For example for Lisbon it would be:

The default response contains many things we don’t really need. We are interested only in a daily forecast, so let’s exclude the following parts:

&exclude=current,minutely,hourly,alerts

We can also change units, so let’s set it to metric system in order to show the temperature in Celsius.

&units=metric

Considering these changes, the endpoint would look like this:

[https://api.openweathermap.org/data/2.5/onecall?lat=38.7267&lon=-9.1403&exclude=current,hourly,minutely,alerts&units=metric&appid={API](https://api.openweathermap.org/data/2.5/onecall?lat=38.7267&lon=-9.1403&exclude=current,hourly,minutely,alerts&units=metric&appid={API) key}

Before we write any code, we can test the endpoint for example in Postman and see what it returns. Below is the result and I highlighted the data we are going to use:

{
  “lat”: 38.7267,
  “lon”: -9.1403,
  “timezone”: “Europe/Lisbon”,
  “timezone_offset”: 0,
  “daily”: [
    {
      **“dt”: 1615723200,**
      “sunrise”: 1615704562,
      “sunset”: 1615747333,
      “temp”: {
        **“day”: 16.03,**
        “min”: 9.38,
        “max”: 18.2,
        “night”: 11.87,
        “eve”: 16.01,
        “morn”: 9.68
      },
    “feels_like”: {
      “day”: 12.26,
      “night”: 9.14,
      “eve”: 12.95,
      “morn”: 5.5
    },
    “pressure”: 1027,
    “humidity”: 49,
    “dew_point”: 5.33,
    “wind_speed”: 3.87,
    “wind_deg”: 31,
    “weather”: [
      {
        “id”: 800,
        “main”: “Clear”,
        “description”: “clear sky”,
        **“icon”: “01d”**
      }
    ],
    “clouds”: 4,
    “pop”: 0,
    “uvi”: 5.33
  },
  ...

2. Display the data

We got data for the daily forecast. The ‘daily’ array contains 8 objects, which means today + 7 days. Assuming we have following HTML, we can write a function that will create an element for each day that we can append to .forecast div.

<section class=”weather”>
  <div class=”container”>
    <div class=”forecast”>
      // here we populate the 7 days forecast
    </div>
  </div>
</section>

Let’s name this function fetchForecast()

fetchForecast = function () {
  var endpoint =
    "https://api.openweathermap.org/data/2.5/onecall?lat=38.7267&lon=-9.1403&exclude=current,hourly,minutely,alerts&units=metric&appid={API key}";
  var forecastEl = document.getElementsByClassName("forecast");

  fetch(endpoint)
  .then(function (response) {
    if (200 !== response.status) {
      console.log(
        "Looks like there was a problem. Status Code: " + response.status
      );
      return;
    }

    forecastEl[0].classList.add('loaded');

    response.json().then(function (data) {
      var fday = "";
      data.daily.forEach((value, index) => {
        if (index > 0) {
          var dayname = new Date(value.dt * 1000).toLocaleDateString("en", {
            weekday: "long",
          });
          var icon = value.weather[0].icon;
          var temp = value.temp.day.toFixed(0);
          fday = `<div class="forecast-day">
            <p>${dayname}</p>
            <p><span class="ico-${icon}" title="${icon}"></span></p>
            <div class="forecast-day--temp">${temp}<sup>°C</sup></div>
          </div>`;
          forecastEl[0].insertAdjacentHTML('afterbegin', fday);
        }
      });
    });
  })
  .catch(function (err) {
    console.log("Fetch Error :-S", err);
  });
};

This is what we do here:

  • We create forecastEl variable to refer to the HTML element

  • We use fetch() method that takes 1 mandatory argument — our endpoint. It returns a Promise that resolves to the Response

  • We get a JSON data (response.json()) and we iterate through the daily array with forEach loop.

  • The next 7 days is what we care about, so we check if (index > 0)

  • daily.dt is the time of the forecasted data, Unix, UTC. To get a readable name (Monday, Tuesday, etc.) we need to convert it:

var dayname = new Date(value.dt * 1000).toLocaleDateString(“en”, { weekday: “long”, });
  • We format the temperature with *toFixed()* method and display it with other data we are interested in in HTML using template literals.

  • Lastly we append the HTML to our page.

3. Performance improvement

There are many ways to optimize a website for speed. We can lazy load the resources for example (Route-based, on interaction, in viewport), use a new CSS techniques like content-visibility etc.

In this example we can focus on fetching the data only after we scroll to the weather element. To do that, we can utilize Intersection Observer API. We query the .weather selector, observe it and once we get close enough by scrolling we call the fetchForecast() function.


document.addEventListener( 'DOMContentLoaded', function() {
  var weather;

  if ( 'IntersectionObserver' in window ) {
    weather = document.querySelectorAll('.weather');

    var weatherObserver = new IntersectionObserver( function( entries, observer ) {
      entries.forEach( function( entry ) {
        if ( entry.isIntersecting ) {
          if (entry.target.classList.contains('weather')) {
            fetchForecast();
          }
        }
      });
    }, {
      rootMargin: '0px 0px -120px 0px'
    });

    weather.forEach(function (s) {
      weatherObserver.observe(s);
    });
  }
});

This way we saved almost 4kB on the initial load.

We defined also rootMargin in pixels which says the call should be triggered ‘x’ pixels before we get to the element.

Summary

In this article we learned how to use OpenWeather API to get a weather forecast data and how to display it on the web using JavaScript. We used native methods like fetch() to get the data and we easily created HTML with template strings.

It’s always good to consider performance implications so in this case we make sure we fetch and process the data only after user scrolls in the viewport. For that we used Intersection Observer API.

More content at **plainenglish.io**