5 Steps to a Serverless Azure Functions Weather App


This is my second post on Azure Functions, but this function was much harder for me to write, and I learned more, so I thought I would share that learning in a new post.

My goal was to create a function that could be called via URL that returned a text weather forecast. I wanted simple text as I have been using the text-based T-UI Launcher on my Android phone (which I love).

Touching the lighting icon on my phone calls a curl to my Aure function. City and state are passed as URL options. I added a date call before and after the function to keep track of the response time. The output looks like this:




Step 1: Beg Someone Who Knows JavaScript to Show You How to Process a URL

I know quite a few programming languages, but JavaScript is not one of them. I Googled for a while trying to find a suitable example to parse a Weather Underground URL. No go. Luckily, I happen to be employed at the #5 Best Place to Work in America and am surrounded by helpful brilliant people. I reached out to one of our internal IT managers for help and, in less than an hour, had a working program and a limited understanding of node.js. Yay.

Step 2: Learn How JSON Works

OK. I now had a node.js JavaScript program that I could run locally that would return a JSON structure. I just did not know how to display the JSON pieces I needed for my weather forecast.  JSON Editor Online was the key here. I pasted the JSON returned from Weather Underground into this site and started to understand the structure as well as how to display just what I wanted in my weather forecast. For instance, after lots of trial and error, I found that obj.forecast.simpleforecast.forecastday[0].conditions represents the text forecast for today.

Step 3: Pay $29 For Excellent Azure Monthly Support

Eventually, I had a node.js JavaScript program that would run on my laptop and return the text weather forecast I wanted. I created a new empty function in Azure Functions and pasted the JavaScript code into the function and ran it. Result - lots of errors I did not understand. My brilliant friends in internal IT know JavaScript very well, but do not use Azure Functions. It was time to ask Microsoft for help. Thankfully, for $29/month I got access to my own personal Support Escalation Engineer, Edison. I couldn't be happier with Edison's support. He modified my code for me to work in Azure Functions. He taught me how to use the Kudu Console to create a working JavaScript npm environment including package.json and required module dependencies. This is one huge advantage of Azure Functions over AWS Lambda. You have a working PowerShell environment where you can run node.js commands like npm. In AWS Lambda, you do not have command line access, so you have to ZIP up a working node.js directory structure and upload.



Step 4: Get the JavaScript Program Running on Azure Functions

(For steps on creating an Azure Function in the console, please see my previous blog post or this Microsoft article.)
My JavaScript program requires one JavaScript module - got. This means that I had to install the got module and any dependencies from the Azure Functions Kudu PowerShell CLI.  Pretty simple, once you figure out the steps:
  • From Azure, go into Platform Features -> Development Tools -> Kudu
  • Once in Kudu, choose Debug Console, PowerShell
  • Cd site\wwwroot
  • Run "npm init" and follow the prompts to create package.json
  • Run "npm install" to create the empty node_modules directory
  • Run "npm install --save got to create the directories for the got module and the got module dependencies
  • Test the code with any variables back in the code window


















I noticed that the first call to the function takes about 10 seconds to run while subsequent calls take less than 1 second. I think this is due to the Azure Functions architecture. AWS Lambda does not have this "first transaction" penalty. I beleive the reason for this is that your Azure functions actually run on a private IIS server in a Windows equivalent of a container. If the container is not used for a specified period of time, the container shuts down. When the function is invoked again, the continer starts up, runs the code and returns the results.

P.S. Here is the Code If You Are Interested


var got = require('got');

module.exports = function (context, req) {

    var url = 'http://api.wunderground.com/api/[put_your_api_key_here]/forecast/q/' + req.query.state + '/' + req.query.city + '.json';

    got(url, { headers:{'Content-Type':'application/json'}}).then(function(response) {

    var obj = JSON.parse(response.body);
        simpleString = obj.forecast.simpleforecast.forecastday[0].date.weekday_short + ': ' + obj.forecast.simpleforecast.forecastday[0].conditions + " " + obj.forecast.simpleforecast.forecastday[0].low.fahrenheit + '-' + obj.forecast.simpleforecast.forecastday[0].high.fahrenheit + '\n' + obj.forecast.simpleforecast.forecastday[1].date.weekday_short + ': ' + obj.forecast.simpleforecast.forecastday[1].conditions + " " + obj.forecast.simpleforecast.forecastday[1].low.fahrenheit + '-' + obj.forecast.simpleforecast.forecastday[1].high.fahrenheit + '\n' + obj.forecast.simpleforecast.forecastday[2].date.weekday_short + ': ' + obj.forecast.simpleforecast.forecastday[2].conditions + " " + obj.forecast.simpleforecast.forecastday[2].low.fahrenheit + '-' + obj.forecast.simpleforecast.forecastday[2].high.fahrenheit + '\n' + obj.forecast.simpleforecast.forecastday[3].date.weekday_short + ': ' + obj.forecast.simpleforecast.forecastday[3].conditions + " " + obj.forecast.simpleforecast.forecastday[3].low.fahrenheit + '-' + obj.forecast.simpleforecast.forecastday[3].high.fahrenheit;

       res = {
                status: 200,
                body: simpleString,
                headers: { 'content-type': 'text/plain' },
                isRaw: true
             };
        context.done(null, res);

    }).catch(function(error) {
        context.error(error)
    });
};

Step 5: Get Weather Via CLI


My Android CLI luncher, t-ui, supports the curl command. I created an alias to a script that gets the weather. The contents of the alias are:

curl --silent https://[azure_function_url]&state=MY_STATE&city=MY_CITY

The output looks like this:

Maynard, MA: Sunny. High 32F. Winds NW at 5 to 10 mph.

Today: Clear 19-32
Fri: Snow 24-26
Sat: Ice Pellets 31-39

Sun: Mostly Cloudy 28-38

I hope you have found this post helpful. I welcome your input.

Comments

Dennis Faucher said…
My pleasure. Glad you enjoyed the post.