Build a Simple Node.js Interactive Web-Based Weather App

Written by

in

In today’s interconnected world, having quick access to weather information is essential. Whether you’re planning your day, traveling, or simply curious about the conditions outside, a weather application is incredibly useful. This tutorial will guide you through building a simple, interactive, web-based weather application using Node.js. We’ll fetch weather data from a free API, display it in a user-friendly format, and make it interactive so users can search for the weather in different cities. This project is perfect for beginner to intermediate developers looking to expand their skills in Node.js, API interaction, and front-end development.

Why Build a Weather App?

Building a weather app is a practical project that allows you to:

  • Learn how to work with APIs (Application Programming Interfaces).
  • Understand how to handle HTTP requests and responses.
  • Practice front-end development with HTML, CSS, and JavaScript.
  • Gain experience in Node.js back-end development.
  • Create a useful application that you can use daily.

This project is designed to be accessible, breaking down complex concepts into manageable steps. By the end, you’ll have a fully functional weather app and a solid foundation for building more complex web applications.

Prerequisites

Before we begin, make sure you have the following installed:

  • Node.js and npm (Node Package Manager): This is the foundation of our project. You can download it from the official Node.js website: https://nodejs.org/.
  • A text editor or IDE: Such as Visual Studio Code, Sublime Text, or Atom.

Step-by-Step Guide

1. Setting Up the Project

First, create a new directory for your project and navigate into it using your terminal or command prompt:

mkdir weather-app
cd weather-app

Initialize a new Node.js project by running:

npm init -y

This command creates a package.json file, which will manage our project’s dependencies.

2. Installing Dependencies

We’ll use a few npm packages to make our lives easier:

  • express: A web application framework for Node.js.
  • node-fetch: A package to make HTTP requests (fetching data from the weather API).
  • dotenv: To load environment variables from a .env file (for storing our API key securely).

Install these packages:

npm install express node-fetch dotenv

3. Setting Up the Server (server.js)

Create a file named server.js in your project directory. This file will contain the server-side logic of our application.

First, let’s import the necessary modules:

const express = require('express');
const fetch = require('node-fetch');
require('dotenv').config(); // Load environment variables

Next, initialize an Express app and set up the port:

const app = express();
const port = process.env.PORT || 3000;

We use process.env.PORT to read the port from an environment variable, which is good practice for deployment. If no environment variable is set, it defaults to port 3000.

Now, let’s set up middleware to serve static files (HTML, CSS, and JavaScript):

app.use(express.static('public')); // Serve static files from the 'public' directory
app.use(express.json()); // Middleware to parse JSON request bodies

This tells Express to serve static files (like our HTML, CSS, and JavaScript) from a directory named ‘public’. The express.json() middleware is used to parse incoming requests with JSON payloads (we’ll use this later when handling user input).

Now, let’s create an API endpoint to fetch weather data. We’ll use the OpenWeatherMap API (you’ll need to sign up for a free API key at https://openweathermap.org/).

Create a .env file in your project root and add your API key:

OPENWEATHERMAP_API_KEY=YOUR_API_KEY_HERE

Here’s the code for the weather API endpoint:

app.post('/weather', async (req, res) => {
  const { city } = req.body; // Get the city from the request body
  const apiKey = process.env.OPENWEATHERMAP_API_KEY;
  const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`;

  try {
    const response = await fetch(apiUrl);
    const data = await response.json();

    if (data.cod === 200) {
      // Successful response
      res.json(data);
    } else {
      // Handle errors from the API
      res.status(data.cod).json({ message: data.message || 'Error fetching weather data' });
    }
  } catch (error) {
    console.error('Error fetching weather data:', error);
    res.status(500).json({ message: 'Internal server error' });
  }
});

This code does the following:

  • It defines an API endpoint at /weather that accepts POST requests.
  • It retrieves the city name from the request body (the user will enter this in our front-end).
  • It constructs the API URL using the city name and our API key.
  • It uses fetch to make a request to the OpenWeatherMap API.
  • It parses the response as JSON.
  • It checks for errors (e.g., invalid city).
  • It sends the weather data back to the client as a JSON response.

Finally, start the server:

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Your complete server.js file should look like this:

const express = require('express');
const fetch = require('node-fetch');
require('dotenv').config();

const app = express();
const port = process.env.PORT || 3000;

app.use(express.static('public'));
app.use(express.json());

app.post('/weather', async (req, res) => {
  const { city } = req.body;
  const apiKey = process.env.OPENWEATHERMAP_API_KEY;
  const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`;

  try {
    const response = await fetch(apiUrl);
    const data = await response.json();

    if (data.cod === 200) {
      res.json(data);
    } else {
      res.status(data.cod).json({ message: data.message || 'Error fetching weather data' });
    }
  } catch (error) {
    console.error('Error fetching weather data:', error);
    res.status(500).json({ message: 'Internal server error' });
  }
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

4. Creating the Front-End (HTML, CSS, and JavaScript)

Create a directory named public in your project root. Inside this directory, create the following files:

  • index.html
  • style.css
  • script.js

index.html

This file will contain the HTML structure of our weather app.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Weather App</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h1>Weather App</h1>
        <div class="search-box">
            <input type="text" id="cityInput" placeholder="Enter city name">
            <button id="searchButton">Search</button>
        </div>
        <div id="weatherInfo"></div>
    </div>
    <script src="script.js"></script>
</body>
</html>

This HTML creates the basic layout: a heading, a search box (input field and button), and a div to display the weather information.

style.css

This file will hold the CSS styles to make our app look good.

body {
  font-family: sans-serif;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  background-color: #f0f0f0;
}

.container {
  background-color: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  text-align: center;
}

h1 {
  color: #333;
}

.search-box {
  margin-bottom: 20px;
}

input[type="text"] {
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
  margin-right: 10px;
}

button {
  padding: 8px 15px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

#weatherInfo {
  margin-top: 20px;
}

.error {
  color: red;
}

This CSS provides basic styling for the layout, search box, and weather information display.

script.js

This file will contain the JavaScript code to handle user interaction and fetch weather data from our server.

const cityInput = document.getElementById('cityInput');
const searchButton = document.getElementById('searchButton');
const weatherInfo = document.getElementById('weatherInfo');

searchButton.addEventListener('click', async () => {
  const city = cityInput.value;
  if (!city) {
    alert('Please enter a city name.');
    return;
  }

  try {
    const response = await fetch('/weather', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ city }),
    });

    const data = await response.json();

    if (response.ok) {
      // Successful response
      displayWeather(data);
    } else {
      // Handle errors
      displayError(data.message);
    }
  } catch (error) {
    console.error('Error fetching weather data:', error);
    displayError('Failed to fetch weather data.');
  }
});

function displayWeather(data) {
  const weatherHTML = `
    <h2>Weather in ${data.name}, ${data.sys.country}</h2>
    <p>Temperature: ${data.main.temp} °C</p>
    <p>Humidity: ${data.main.humidity}%</p>
    <p>Description: ${data.weather[0].description}</p>
    <p>Wind Speed: ${data.wind.speed} m/s</p>
  `;
  weatherInfo.innerHTML = weatherHTML;
}

function displayError(message) {
  const errorHTML = `<p class="error">${message}</p>`;
  weatherInfo.innerHTML = errorHTML;
}

This JavaScript code does the following:

  • It gets references to the input field, search button, and weather information div.
  • It adds an event listener to the search button. When the button is clicked:
  • It gets the city name from the input field.
  • It sends a POST request to our /weather endpoint with the city name in the request body.
  • It parses the JSON response from the server.
  • If the response is successful, it calls the displayWeather function to display the weather information.
  • If there’s an error, it calls the displayError function to show an error message.
  • The displayWeather function takes the weather data and generates HTML to display it.
  • The displayError function displays error messages.

5. Running the Application

To run the application, open your terminal, navigate to your project directory, and run:

node server.js

This will start the Node.js server. Open your web browser and go to http://localhost:3000 (or the port you configured). You should see the weather app. Enter a city name and click the search button to see the weather information.

Common Mistakes and How to Fix Them

  • API Key Issues:
    • Mistake: Forgetting to include your API key or using an incorrect key.
    • Fix: Double-check your .env file and ensure you’ve set the OPENWEATHERMAP_API_KEY variable with your actual API key. Also, verify that the API key is active on the OpenWeatherMap website.
  • CORS Errors:
    • Mistake: Receiving CORS (Cross-Origin Resource Sharing) errors, which prevent your front-end from accessing your back-end API.
    • Fix: If you’re running your front-end and back-end on different domains (e.g., different ports), you might encounter CORS errors. To fix this, you can install the cors package and use it as middleware in your server.js file:
    npm install cors

    And add to your server.js file:

    const cors = require('cors');
    app.use(cors());
  • Incorrect API Endpoint:
    • Mistake: Using the wrong API endpoint URL or parameters.
    • Fix: Carefully review the OpenWeatherMap API documentation to ensure you’re using the correct endpoint and parameters (e.g., city name, units).
  • Network Errors:
    • Mistake: Problems with network connectivity.
    • Fix: Make sure you have a working internet connection. Check your browser’s developer tools (Network tab) to see if the requests to the API are failing.
  • Incorrect File Paths:
    • Mistake: Errors in the file paths of your HTML, CSS, or JavaScript files.
    • Fix: Verify that the paths in your HTML (e.g., <link rel="stylesheet" href="style.css">) correctly point to your CSS and JavaScript files. Make sure the public folder is in the correct place.
  • Typographical Errors:
    • Mistake: Small typos in your code (e.g., variable names, function names).
    • Fix: Carefully review your code for any typos. Use your browser’s developer tools (Console tab) to identify and fix any JavaScript errors.

Key Takeaways

  • You’ve successfully built a simple weather application using Node.js, Express, and a public API.
  • You’ve learned how to make HTTP requests, handle API responses, and display data in a user-friendly format.
  • You’ve gained experience with front-end development (HTML, CSS, JavaScript) and back-end development (Node.js).
  • You now have a basic understanding of how to use environment variables for security.

FAQ

  1. Can I use a different weather API?

    Yes, you can easily adapt the code to use a different weather API. You’ll need to sign up for an API key with the new provider and modify the API endpoint URL and data parsing logic in your server.js file to match the new API’s documentation.

  2. How can I deploy this app?

    You can deploy your app to platforms like Heroku, Netlify, or AWS. You’ll need to configure your environment variables (API key) and possibly adjust the build process based on the platform’s requirements. Remember to include a Procfile for Heroku deployments and configure build commands for other platforms.

  3. How can I add more features?

    You can add features such as displaying weather forecasts for the next few days, adding a location search using geolocation, or implementing user authentication. You can also improve the UI/UX with more advanced CSS and JavaScript.

  4. How do I handle errors more gracefully?

    Improve error handling by adding more specific error messages, handling different HTTP status codes, and providing better feedback to the user. Consider implementing a loading indicator while fetching data and displaying error messages in a more user-friendly way.

  5. What are some other useful Node.js packages for web development?

    Some other useful packages include body-parser (for parsing request bodies), morgan (for logging HTTP requests), nodemon (for automatically restarting the server during development), and various templating engines like pug or ejs for dynamic HTML generation.

This project is a starting point. From here, you can explore more advanced features, experiment with different APIs, and enhance the user interface to create a more sophisticated weather application. The skills you’ve acquired in this tutorial will be invaluable as you continue your journey in web development.