Downloading Weather Data aka Promises for the Dummies

Image credit: Pexels

Downloading Weather Data aka Promises for the Dummies

Emilia Szymańska bio photo By Emilia Szymańska Comment

Hi guys! The first (and easiest) thing I have to do in my project is downloading weather data from some API. My first thought was AccuWeather, since I use it every day in my phone. Unfortunately it turned out that is kind of expensive. Well, at least it was about two weeks ago when I was checking. At the time of writing this post, there is no pricing table on the page.

I searched for another solution and found OpenWeatherMap. The “Open” part of the name sounded promising and it turned out that there is free plan that will be totally viable for me.

1

I requested an API key and got an email telling me I can access it, so I logged in to OpenWeatherMap and it was waiting there for me. To try if it’s working, I quickly wrote a function to download weather data for Warsaw (I’m using Express as a web application framework):

let weatherTest = (req: express.Request, res: express.Response): void => {
    request.get({
        url: openWeatherUrl,
        headers: { "Content-Type": "application/json" },
        qs: { id: 756135, appid: "<< api key >>", units: "metric" }
    }, function (error, response, body) {
        if (error) {
            res.json(error);
        }
        else {
            let bodyParsed = JSON.parse(body);
            res.json(bodyParsed.main.temp);
        }
    });
};

Firstly, of course, I needed to install the request module using command

npm install --save request

The save parameter indicates that this package definition should be added to the package.json file.

WeatherTest is a function that I simply wired up to the route, so it accepts two parameters – request and response (do not confuse it with the request to the weather api – this is a request to my server). The request parameter contains all the data that was sent. I do not check any parameters right now, as I happily hardcoded the city id.

The response is an object that is used to send… a response! ;) There are several things we can send back, for example the response code. I chose to send json with either information about the error or the desired temperature.

Remember – you can only send one response to the request. It seems as a no brainer, but it might happen that you’ll fuck something up with conditions and try to send it the second time – it will throw a nasty exception “Can’t set headers after they are sent.” in that case, which doesn’t really clearly indicate what the cause of the problem is.

So, the temperature. Quick look at the OpenWeatherApi documentation and we get to know what the API endpoint is and how can we ask for the weather. We just need to send one GET request with our parameters – city, app key and units type for the temperature (default is Kelvin). As for the city – docs suggest to use the city codes to avoid ambiguity, there is a link with all the cities on the docs site. We also need to append our app key, which can be found on the website after we log in.

And voila:

2

I didn’t know it was so cold in Warsaw. :) The response is valid, I checked it with AccuWeather. ;D

Obviously, this solution works but is not written nicely. I just put this function right into the file in the routes folder (at least it’s in a separate routes file!).

Cleaning that shit

You know what? If I wasn’t coding this publicly and I would like to focus more on the electronic part of the project, I might just leave it like that. I want to have a way of downloading Warsaw temperature and that does it perfectly.

But of course, as I actually think that one day someone other than me might like to use it, it’s time to refactor that little function. So here’s how the TypeScript class looks like:

export default class TemperatureDownloader {
    private readonly openWeatherUrl: string = "http://api.openweathermap.org/data/2.5/weather";
    private readonly appKey: string = "<<>>";
    private readonly defaultUnits: string = "metric";

    GetCurrentByCityId(cityId: number, units: string = this.defaultUnits): Promise<number> {
        return new Promise<number>((resolve, reject) => {
            request.get({
                url: this.openWeatherUrl,
                headers: { 'Content-Type': 'application/json' },
                qs: { id: cityId, appid: this.appKey, units: units }
            }, function (error, response, body) {
                if (error) {
                    reject(error);
                }
                else {
                    let bodyParsed = JSON.parse(body);
                    if (bodyParsed != null && bodyParsed.main != null) {
                        resolve(bodyParsed.main.temp);
                    } else {
                        reject("Wrong response from the API!");
                    }
                }
            });
        });
    };
}

It is exported as default and contains only one method for now – the one for downloading the temperature. The method returns a Promise. Promises for me are the perfect remedy for the callback hell, common in Node.js applications, where almost every operation is asynchronous. I struggled to understand them for a long time, sticking to my opinion that async.js library is good enough, but once I started to use Promises every day, I really fell in love with them.

Promise is a function that takes two arguments – callback for the good situation and callback for the fuckup situation. Inside the Promise body we do our stuff and when it’s time to return some results, we use these functions accordingly. The real beauty can be seen when we’re using this method:

let weatherTest = (req: express.Request, res: express.Response): void => {
    var downloader = new TemperatureDownloader();
    downloader.GetCurrentByCityId(756135)
        .then(temp => res.status(200).json(temp))
        .catch(error => res.status(500).json(error));
};

I guess it’s self-explanatory. :) This example is really simple, but you know what? I really wish someone showed me something like that when I was struggling to understand what Promises are in fact and how to use them.

It all gets really exciting, when we add to this the fact, that Promises can be chained. We just need to return new Promise inside the then function. Let’s imagine our TemperatureDownloader can download real and feel temperature:

let weatherTest = (req: express.Request, res: express.Response): void => {
    var downloader = new TemperatureDownloader();
    let realTemp: number;
    downloader.GetRealByCityId(756135)
        .then(temp => {
            realTemp = temp;
            return downloader.GetFeelByCityId(756135);
        })
        .then(temp => {
            res.status(200).json({real: realTemp, feel: temp});
        })
        .catch(error => res.status(500).json(error));
};

Now, instead of simply sending a response, we invoke another request by chaining the promises. The best thing about this example is the catch block. It works for all chained Promises, so whenever any of them returns an error, it will end up there. Also the exceptions land there, which is really great when we remember how difficult is catching exceptions in vanilla JavaScript.

I’m leaving the WeatherTest route for testing purposes, but I’m not developing it more (e.g. no request parameters for the city), as my server needs to download the data accordingly to some schedule and then just pass it to the application that steers the lights.

That’s it for today. Take care and remember that Promises are cool. :)

And as always - like my fan page or follow me on Twitter to get notified when future posts appear. (。◕‿◕。)

comments powered by Disqus