Understanding non-blocking I/O and asynchronous code is the biggest, most often misunderstood problem for an experienced developer who is trying to learn Node. This is partially due to the fact that most of the programming is taught and done in the synchronous way. In this quick and easy article, we will walk through the creation of an asynchronous application which will give a forecast for a given city.

Opening a web browser is a slower process than firing a command from your terminal. Weather websites have many ads which distract and slow down the process. It’s like finding your friends in Disneyland when you’re lost and have no cell phone coverage. With this command-line tool, you’ll be able to get the weather forecast in just a few moments in a no-B.S. format—only what you need.

Or maybe you’re working for NASA 🚀 and you need to implement a CLI program which will give a weather forecast for a particular city, there’s no Windows 10 in space! There’s just a plain old command prompt. It’s a CLI app because astronauts are not expected to know Node so they cannot modify the code and the computing resources are scarce so we cannot use a full-blown GUI environment (like a browser).

Steps to Build a Weather App

  1. Get OpenWeatherMaps API key
  2. Create a JSON config file and add the key to it
  3. Create a module which does the async request to a third-party service (event emitters)
  4. Implement a CLI app that takes the city name from a user and calls the module
  5. Test your app by typing node weather Seattle

In this article, you will create modules, make an HTTP request, read CLI arguments, work with observers (event emitters), and the OpenWeatherMaps API (to get the forecast).

Here’s a bird’s-eye view of what you’ll need to create:

  • config.json: A file with an API key
  • weather-module.js: A module with a function that makes a call based on the city name and provides the forecast as a JavaScript/Node object
  • weather.js: A CLI runner which uses the module, takes the CLI argument (city name) and prints the results nicely

First, register with OpenWeatherMaps API by going to their website and clicking “Sign up”.

Once you’ve signed up for the API, go to your home at http://home.openweathermap.org, locate your API key, copy it, and paste/save it into config.json which you need to create in a new folder (your project folder).

{
  "openWeatherApiKey": "your key goes here"
}

Then, create weather-module.js and implement a module that provides a forecast from OpenWeatherMaps. There are many ways to implement a module in Node, but because we need to accept the city name from the command-line input, we will be using the method called “exporting a function”.

The purpose of this function is to take the API key, city name, and a callback function, and then make an HTTP request to OpenWeatherMaps, parse response body, and finally, invoke callback with the results when everything is done. There are no fancy libraries for making HTTP calls like axios, request or superagent. We just use a plain http module which uses event emitters.

var http = require('http')
module.exports = function(apiKey, city, callback){
  city = encodeURIComponent(city)
  http.get({
      host: 'api.openweathermap.org',
      path: `/data/2.5/forecast/?q=${city}&APPID=${apiKey}&units=imperial`
    }, function(response) {
      // Continuously update stream with data
      var body = ''
      response.on('data', function(d) {
        body += d
      })
      response.on('end', function() {
        // Data reception is done, do whatever with it!
        var parsed = JSON.parse(body)
        // console.log(parsed)
        callback(null, parsed)
    })
  })
}

The idea behind event emitter data is that it will fire on every new line of the response body (payload). That’s why we concatenate body by using body += d. When the response has reached its end, we parse JSON and invoke callback to get to the main program (different file weather.js). That’s because it’s better to have the returned data as an object.

So, we utilize http.get or http.request to request the data. You can look up the format of the forecast URL request at OpenWeather forecast.

It’s a good idea to encode the city name to allow it to be used in the URL for the API request, because a city name might have spaces and other special characters. This is how you can encode a string with encodeURIComponent():

path: `/data/2.5/forecast/?q=${encodeURIComponent(city)}&APPID=${apiKey}&units=imperial`

To make the units imperial (F), we pass units=imperial.

Next, create weather.js which will be the CLI app runner and the main program which consumes our module weather-module.js written above. Yes, it will require our module, get the city name from the CLI input, get the API key from the JSON file, and then print the results into the terminal.

var city = process.argv[2]

if (!city) return console.error('Please provide city')

var weather = require('./weather-module')

var apiKey = require('./config.json').openWeatherApiKey

weather(apiKey, city, function(error, results) {
  results.list.forEach(function(forecast){
    console.log(forecast.dt_txt, forecast.main.temp, 'F')
  })
})

For the output, we use the following formats: dt_text (date and time as text), main.temp (temperature) and F (units). You will need to become familiar with the response structure first. Consult the OpenWeather API documentation or make a few test requests to see what are the results of those calls.

Show an error “Please provide city” if the city is missing from a CLI command.

In the end, these commands should work just fine:

node weather London
node weather "New York"
node weather "San Francisco"

The example of the output with the forecast every three hours:

2016-03-07 15:00:00 40.49 F
2016-03-07 18:00:00 44 F
2016-03-07 21:00:00 46.67 F
2016-03-08 00:00:00 40.93 F
2016-03-08 03:00:00 39.41 F
2016-03-08 06:00:00 40.44 F
2016-03-08 09:00:00 40.94 F
2016-03-08 12:00:00 39.74 F
2016-03-08 15:00:00 43.57 F
2016-03-08 18:00:00 49.47 F
2016-03-08 21:00:00 53.57 F
2016-03-09 00:00:00 53.22 F
2016-03-09 03:00:00 53.62 F
2016-03-09 06:00:00 53.31 F
2016-03-09 09:00:00 52.19 F
2016-03-09 12:00:00 50.43 F
2016-03-09 15:00:00 51.57 F

The source code along with a test can be found at https://github.com/azat-co/weather-app.

Wrap Up

In this article, you’ve observed (and learned, right?) the following things:

  • Making async HTTP calls with event emitters
  • URL encoding strings
  • Extracting CLI arguments
  • Implementing an async module with “export as function” pattern
  • Using string literal templates (path for http.get)
  • Loading JSON with require()

Here are some useful links:

In this article, we implemented asynchronous code to make a request to an external service. We didn’t use any of the npm modules because Node core has everything we need (for this project).

One of the common patterns which is everywhere in the Node core is event emitters. If you would like to know more about event emitters and other Node patterns, learn more about my course Node Patterns: From Callbacks to Observer.