We’ll learn how to create a simple weather app using the openWeatherMap
API where the user will enter a ‘city’ and we will display the current temperature, weather condition, humidity value, and name of the city. Our goal here is to experiment with the integration of Node JS
on the server-side and use handlebars
as the templating engine. This will add variety to the syntax of templates, partials, and views that we’ll create to render the app on the webpage.
Let’s start by creating a new app in the code editor of our choice, we’re using VS code and we’ll navigate the terminal of our application and initialize the node by typing the following:
npm init
This will create a package.json
file in the root of our app indicating the metadata (information) related to our app and all the dependencies required in this app will also be handled in this file.
Next, we’ll install all the dependencies (npm packages) that we’ll use throughout the project. Their uses and the commands required to install are given below:
express
: It is the standard server-side framework for Node JS
. It is required to have this installed.npm i express
handlebars
template engine: The templating language framework that we will use to generate the HTML
for our views/pages.npm i hbs
request
: We need the request module to make the HTTP
calls and get the required data.npm i request
Now, we’ll create an src
folder in the root directory of our app and create an app.js
file there. We will require the dependencies and also place the initial code for the server to setup:
const express = require('express');
const req = require('express/lib/request');
const app = express();
const port = process.env.PORT || 3000;
// create a default route for our server
app.get('/', (req,res)=>{
res.send('This is the default Route...')
})
app.listen(port, () => {
console.log('Server is up and running on port: ', port)
});
The code above will make sure that the server is running on port 3000
.
openWeatherMap
API keyPlease note that for this app we have to login to openweathermap website and generate an API key which will be used in the base URL of this app.
We have defined the base_url
and secret key
in a separate file called config.js
in an object called constants:
Note: We should add our own
secret_key
generated from the openweathermap website in order to generate the weather data from the executable code widget at the bottom of this shot .
Now, we’ll create another file weatherData.js
in the root directory and call the API to retrieve the current temperature, name of the city, weather description, and humidity. For this purpose, we’ll import the request module since we made an HTTP request to the openWeatherMap
API and then fetch the data from API.
const request = require('request')
const constants = require('./config')
const weatherData = (address, callback) => {
const url = constants.openWeatherMap.BASE_URL + encodeURIComponent(address) + `&appid=` + constants.openWeatherMap.SECRET_KEY
request({url,json:true},(error,{body})=>{
// console.log(body)
if(error){
callback(`Can't fetch the data`,undefined)
} else {
callback(undefined, {
temperature: body.main.temp,
description: body.weather[0].description,
cityName:body.name
})
}
})
}
module.exports = weatherData;
The code above is getting the data from the API in a callback function and targeting the response object to extract the required data:
temperature: body.main.temp
description: body.weather[0].description
cityName:body.name
Now, we can call the weatherData
method in src/app.js
to have access to the response.
const weatherData = require('../weatherData')
Also, we’ll define a /weather
route here where we can console.log
the results obtained:
// This is the Route to get the weather data
// localhost:3000/weather?address=chicago
app.get('/weather', (req,res) => {
const address = req.query.address
if(!address){
return res.send({
error: "Please enter a location to search weather"
})
}
weatherData(address,(error, {temperature, description,cityName}) => {
if(error){
return res.send({
error
})
}
console.log(temperature,description,cityName)
res.send({
temperature,
description,
cityName,
})
})
})
Also, we will be creating two folders under templates:
partial
to be used on different pages in our app like header/footer.We need to specify the views and partials path in src/app.js
as follows:
const hbs = require('hbs');
const path = require('path');
const port = process.env.PORT || 3000;
// specifying the path to our public folder having static assets
const publicStaticDirPath = path.join(__dirname,'../public')
const viewsPath = path.join(__dirname,'../templates/views')
const partialsPath = path.join(__dirname,'../templates/partials')
app.set('view engine','hbs');
app.set('views', viewsPath);
hbs.registerPartials(partialsPath)
app.use(express.static(publicStaticDirPath))
Note: We also have a
public
directory in theroot
of our app where we’ll define all the static assets likestyle.css
andapp.js
.
Now, let’s move to the views
folder and create an index.hbs
file where we’ll be defining the HTML for the index page using handlebars
templating engine.
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
<link rel="stylesheet" href="css/style.css"/>
</head>
<body>
<div>
{{!-- > refers to the partial: header --}}
{{>header}}
<article>
<h3>Please enter the location below:</h3>
<form><input placeholder="location..." type = "text"/><button>Seach</button>
</form>
<div>
<div><span></span></div>
<div>
<div></div>
<div></div>
<div></div>
</div>
</div>
<div></div>
</article>
</div>
<script src="js/app.js"></script> <!-- absolute path -->
</body>
</html>
We now move on to the app.js
file which is created in the public/js
folder where we’ll select the elements from the DOM and render the output on the screen.
Note: The
style.css
file is not included here but it can be downloaded from thestyle.css
file from SPA code widget and include the styles to the app.
var fetchWeather = "/weather";
const weatherForm = document.querySelector('form');
const search = document.querySelector('input');
// const weatherIcon = document.querySelector('.weatherIcon i');
const weatherCondition = document.querySelector('.weatherCondition');
const tempElement = document.querySelector('.temperature span');
const locationElement = document.querySelector('.place');
const humidityElement = document.querySelector('.humidity');
const dateElement = document.querySelector('.date');
const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
dateElement.textContent = new Date().getDate() + ", " + monthNames[new Date().getMonth()].substring(0, 3) + " " + new Date().getFullYear();
weatherForm.addEventListener('submit', (event) => {
event.preventDefault();
locationElement.textContent = "City not found!";
tempElement.textContent = "";
weatherCondition.textContent = "";
const locationApi = fetchWeather + "?address=" + search.value;
fetch(locationApi).then(response => {
response.json().then(data => {
if(data.error) {
locationElement.textContent = data.error;
tempElement.textContent = "";
weatherCondition.textContent = "";
} else {
locationElement.textContent ='City: ' + data.cityName;
tempElement.textContent = (data.temperature - 273.15).toFixed(2) + String.fromCharCode(176);
weatherCondition.textContent = 'Weather Condition: ' + data.description;
weatherForm.reset();
}
})
});
})
The code here is self-explanatory in which we fetch the /weather
route and render the output object into different divs
on the webpage. Once the output is displayed, we reset the form so that the user can type in another search value.
Please note that the
openWeatherMap
API returns thetemperature
in Kelvin. Therefore we would have to subtract273.15
from the output so that the temperature is displayed in Celsius.
We also display the current date by calling the javascript
standard new Date()
method. Since the getMonth()
call will return a value from 0–11
, we want to display the name of the month so we store the names of the months in an array and retrieve the name of the month from the array depending on the result of new Date().getMonth()
method.
The locationApi
key is actually the endpoint that includes the base route, such as /weather
and then the location
which needs to be passed as the query parameter. This will make a call to the weatherData
function and return our desired result.
const constants = { openWeatherMap:{ BASE_URL:'https://api.openweathermap.org/data/2.5/weather?q=', SECRET_KEY:'c3ced497d87fe48e4beb953b02da8b21' } } module.exports = constants;
Free Resources