Azure Functions, APIs, and the Weather
Tags: azure, node, html, tutorial
Published: 2021-04-01 22:13:04

I wanted to create a program that answers the question “What should you do tomorrow?” based on the weather forecast. The goal was to help people come up with creative things to do tomorrow and plan out their day. Often, we fall back on the same activities or, if it's a nice day, find out we're too late to book something. Then we have regrets.

This was one of the first multi-layered coding projects I’d ever tackled, but it’s pretty simple with the right API and Azure Functions. In this tutorial blog, I’ve split the work into three parts: the webpage, the HTTP trigger, and the JavaScript.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zouug8phcfq20sygvcf2.png)


### What you’ll need:

- [A Microsoft Azure subscription](https://azure.microsoft.com/en-us/) (you can get one for free for a limited time)
- An account at [openweather.org](https://openweathermap.org) (this is totally free for the API we’re using unless you want to upgrade)
- [Visual Studio Code](https://code.visualstudio.com) (and to make things easier, install the Live Server extension – for testing your webpage along the way – the Azure Functions extension, and the Azure App Service extension)

## Part #1: The Webpage

This is the easiest part because the webpage is relatively simple. The most important section is creating the `div` and `form` elements for the submission form. You need one overall `div` (I used the id `“container”`), inside of which is a hidden div (id `“hidden-weather”`) and a form element (id `“zipcode-form”`).

```html
<div id="container">

        <div id="hidden-weather" type="hidden"></div>
        
        <form id="zipcode-form" onsubmit="handle(event)">

	            

        </form>

</div>
```

Leave the `onsubmit` part for later – that comes with the JS, which is Part #3.

Inside the `form` element add two `input` tags. The first creates the zip code input box, and the second creates the submit button, which activates the whole process, with the HTTP trigger function and the API.

```html
<input type="text" name="zipcode" id="zipcode-input" accept="5" placeholder="Enter zip code">
<input size="100" type="submit" value="Get recommendations!" id="submit-button-style"></input>
```

The rest of the code in this section formats the webpage. The code below would be placed inside the `div` with the id `"container"`.

```html
<div id="weather-result">

	<center>
	<h3>Weather Forecast for Tomorrow:</h3>
	</center>

</div>

  <br>
  <br>

<div id="recommendations-result">
        
	<center>
	<h3>Recommendations:</h3>
	</center>

</div>
```

Now that we have the user interface complete, let's code the Azure function the user will trigger.

## Part #2: Azure Functions

This part can get a little complicated because you’re dealing with a lot of things at the same time. I’m just going to cover the most important parts.

Create a Function App resource – once the resource is deployed, create a new HTTP trigger function. Don’t open the trigger yet, though, because you need to install an `npm` package in the console. Under Development Tools, open the console and install `node-fetch`. This makes calling the API a lot simpler.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o2s04t66sdoklwix2uju.png)

Go back to your HTTP trigger and open the Code + Test tab. Create a `const` outside of the async function called `fetch`. 

```jsx
const fetch = require('node-fetch');
```

This lets you use the `npm` package that was installed earlier. 

We'll then define three constants: the first deals with the zip code, while the next one calls the API, and the final one formats the forecast data as a JSON file. 

```jsx
module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    const zipcode = (req.query.zipcode || (req.body && req.body.zipcode));
    const apiResult = "";
    const jsonResult = await apiResult.json();

    context.res = {
        // status: 200, /* Defaults to 200 */
        body: jsonResult
    };
}
```

Let's take a look at the `apiResult` constant a bit more closely because that's the most important one.

In your Open Weather account, go to the free 5 day, 3-hour forecast API documentation page here: [https://openweathermap.org/forecast5#zip](https://openweathermap.org/forecast5#zip). Go to the “by ZIP code” section and copy the posted link. This link calls the 5 day, 3-hour forecast API.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qbfjxh6xuthuuw53dcnq.png)

```jsx
api.openweathermap.org/data/2.5/forecast?zip={zip code},{country code}&appid={API key}
```

Back in the HTTP trigger, let's modify our `apiResult` using this information, with `await fetch ()` and the URL you just copied.

```jsx
const apiResult = await fetch ("https://api.openweathermap.org/data/2.5/forecast?zip={zip code}");
```

Then, let's make sure it's actually going to use the zip code entered when calling the API, by replacing the placeholder `{zip code}` with our variable `zipcode`:

```jsx
const apiResult = await fetch ("https://api.openweathermap.org/data/2.5/forecast?zip=" + zipcode);
```

Next, let's add a few parameters to limit the hours to a full day (i.e. `cnt`(or "count") as `8`, for 8 3-hour segments) and use imperial units (instead of scientific ones, like Kelvin).

```jsx
const apiResult = await fetch ("https://api.openweathermap.org/data/2.5/forecast?zip=" + zipcode + "&cnt=8&units=imperial");
```

Finally, insert your Open Weather API key at the end, by tacking on `&appid=`, followed by your API key.

## Part #3: The JavaScript

I’m not going to explain all of the JS either, but I am going to describe how to access certain information and call the trigger function.

```jsx
async function handle(event) {
  event.preventDefault();

  var zipcode = document.getElementById("zipcode-input").value;
  console.log(zipcode);
  const resp = await fetch(
    "https://weatherapifunction.azurewebsites.net/api/WeatherAPI?zipcode=" +
      zipcode,
    {
      method: "POST",
    }
  );

  var data = await resp.json();
  console.log(data);

  const weatherForecastForTommorowNoon = data.list[6];
  const weatherForecastCity = data.city;

var output;

// Abbreviated algorithm
if(weatherForecastForTommorowNoon.pop >= .01){
  output = "string of rainy day activities";
} else if((weatherForecastForTommorowNoon.pop >= .01) && (weatherForecastForTommorowNoon.weather[0].description == "snow")){
  output = "string of snowy day activities";
}

  var weatherRegular = `
      <!Forecast data from API>
			`;

  var recommendations = `
      <p>${output}</p>
  
  `;

  $("#weather-result").html(weatherRegular);
  $("#recommendations-result").html(recommendations);
}
```

The most important section of the code above is the `resp` constant. It calls the Azure trigger function using the trigger’s URL and sends the zip code entered on the website to the trigger function (by accessing the `form` element `“zipcode-input”` created earlier using `document.getElementById`).

```jsx
async function handle(event) {
  event.preventDefault();

  var zipcode = document.getElementById("zipcode-input").value;
  console.log(zipcode);
  const resp = await fetch(
    "https://weatherapifunction.azurewebsites.net/api/WeatherAPI?zipcode=" +
      zipcode,
    {
      method: "POST",
    }
);
```

Now, the entered zip code is run through the trigger function and used when the API is called. It uses `POST` rather than `GET` method, as the zip code is being sent to the Azure trigger function.

Notice `console.log(data)` – now that the data is logged in the console, we can access it using the next two constants. Const `weatherForecastForTomorrowNoon` accesses tomorrow's three-hour forecast data from 1 pm to 4 pm. The next constant accesses the city outputted in the console – it was only used in displaying the general forecast on the website, not in generating activity recommendations.

```jsx
console.log(data);

const weatherForecastForTommorowNoon = data.list[6];
const weatherForecastCity = data.city;
```

Next comes the algorithm – basically, create a list of activities, and then match weather to those activities (you need snow for sledding and skiing, sun and wind for sailing and flying a kite). Create if/else statements for these conditions and activities – if `weatherForecastForTomorrowNoon.{enterWeatherConditionHere}` is `<`, `>`, or `=` a certain temperature or measurement, make the variable `output` equal a string with the activities that are recommended based on the weather conditions.

```jsx
// Abbreviated algorithm
if(weatherForecastForTommorowNoon.pop >= .01){
  output = "string of rainy day activities";
} else if((weatherForecastForTommorowNoon.pop >= .01) && (weatherForecastForTommorowNoon.weather[0].description == "snow")){
  output = "string of snowy day activities";
}
```

After you have created all of these conditions (based on humidity, temperature, wind speed, and precipitation) create four solely weather-based if/else statements that cover all temperatures possible (but no other weather conditions). There may be a day without wind or precipitation, but there will always be temperature.

```jsx
else if(weatherForecastForTommorowNoon.main.temp <= 30){
  output = "string of cold activities";
} else if((weatherForecastForTommorowNoon.main.temp >= 31 && weatherForecastForTommorowNoon.main.feels_like <= 60) && weatherForecastForTommorowNoon.pop == 0){
  output = "string of chilly day activities";
}

//Add more conditionals to the end
```

Now there are no holes in the algorithm. Add these temperature-based statements to the end of your conditionals so that the forecast data is run through these last (because these are the most general statements, and conditionals should always be ordered with the most specific statement first and the most general statement last).

## Final Result

Your project should now look something like this: [https://github.com/TheDirector23/come-rain-or-shine](https://github.com/TheDirector23/come-rain-or-shine)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zwf5foieyj7hwj2ze1qc.png)

Of course, you can always add improvements! The recommendations could be customized based on user inputs (location, personality questions, preferred activities). The recommendations could also be linked to local businesses based on the entered zip code as well (a local marina might be linked to a recommendation to go sailing). Other locations and recommendations could be offered if the conditions aren't great.

That's about it! Hopefully now you're ahead of the weather when it comes to planning for tomorrow.