Weather Forecast App using MERN Stack
This project is a simple weather application built using React.js for the frontend and Node.js with Express.js for the backend. It allows users to check the current weather and forecast for a specific city. The backend retrieves weather data from the OpenWeatherMap API and stores it in a MongoDB database. The frontend displays the weather information and forecast in a user-friendly interface.
Output Preview: Let us have a look at how the final output will look like.
Prerequisites
Approach to Create Weather Forecast App
Backend:
- Uses Express.js to set up RESTful API endpoints.
- Utilizes mongoose to connect to a MongoDB database and store weather data.
- Retrieves weather data from the OpenWeatherMap API based on user requests.
Frontend:
- Uses React.js to create a user-friendly interface.
- Provides an input field for users to enter the city name.
- Retrieves weather data from the backend API and displays it to the user.
- Shows both current weather and forecast information.
Steps to Create the Project
Steps to Create the Backend
Project Structure:
Step 1: Create a directory for project
mkdir server
cd server
Step 2: Initialize npm:
npm init -y
Step 3: Install Dependencies:
npm install express mongoose cors axios
Step 4: Create a file named index.js & write the following code.
//server/index.js
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const axios = require('axios');
const app = express();
const PORT = process.env.PORT || 5000;
// Replace 'YOUR_MONGODB_URI' with your actual MongoDB connection URI
const MONGODB_URI = 'YOUR_MONGODB_URI/weatherforecast';
mongoose.connect(MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true });
app.use(cors());
app.use(express.json());
// Create a Mongoose model for weather data
const WeatherData = mongoose.model('WeatherData', {
city: String,
country: String,
temperature: Number,
description: String,
icon: String,
});
// Route to handle storing weather data
app.post('/api/weather', async (req, res) => {
try {
// Extract weather data from request body
const { city, country, temperature, description, icon } = req.body;
// Create a new document using the WeatherData model
const weatherData = new WeatherData({
city,
country,
temperature,
description,
icon,
});
// Save the weather data to the database
await weatherData.save();
// Respond with success message
res.json({ message: 'Weather data saved successfully' });
} catch (error) {
console.error('Error saving weather data:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
// Start the server
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Run Backend Server:
node index.js
DATABASE OUTPUT:
Steps to Create the Frontend:
Project Structure:
Step 1: Set up React frontend using the command.
npx create-react-app client
cd client
Step 2: Install the required dependencies.
npm install axios
Step 3: Create Frontend Components:
- Modify or replace the src/App.js and src/App.css and other files with code given as below
- In app.js if that API key is not working generate from openweathermap.org and replace it.
/*App.css*/
@import url('https://fonts.googleapis.com/css2?family=Concert+One:wght@300&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@700&display=swap');
html {
height: 100%;
}
body {
background: whitesmoke;
padding: 1rem 5rem;
font-family: 'Roboto', sans-serif;
text-transform: capitalize;
background: linear-gradient(45deg, rgba(183, 204, 248, 0.717), rgba(7, 43, 127, 0.679)), url('https://img.freepik.com/free-photo/blue-sky-with-cloud-closeup_150588-124.jpg?t=st=1710145731~exp=1710149331~hmac=312231e34af28df68a64a9b006a4a627d0f623f0ba6e19eb23e5aceae935e41e&w=360');
background-repeat: no-repeat;
background-position: center;
background-size: cover;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
color: rgb(214, 147, 45);
text-decoration: underline;
font-family: 'Orbitron', sans-serif;
}
#input {
font-size: 18px;
padding: 5px 10px;
outline: none;
border: none;
border-radius: 15px;
background: aliceblue;
}
#search {
background: none;
padding: 5px 20px;
color: aliceblue;
outline: none;
background: cadetblue;
font-size: 17px;
border-radius: 15px;
cursor: pointer;
border: none;
}
.weather {
text-align: center;
color: aliceblue;
}
#city {
font-size: 30px;
}
.weather img {
width: 120px;
height: 120px;
border-radius: 50%;
background: rgba(240, 248, 255, 0.408);
}
#temperature {
font-size: 50px;
margin: 0;
margin-left: 30px;
margin-bottom: 10px;
}
.temp-box {
display: flex;
align-items: center;
justify-content: center;
margin: 30px 0;
}
#clouds {
font-size: 20px;
background: rgba(153, 205, 50, 0.778);
padding: 2px 20px;
border-radius: 15px;
}
main {
display: grid;
grid-column-gap: 25px;
grid-template-columns: 1fr 5px 1fr;
align-items: center;
margin: 0 50px;
color: white;
}
.next {
display: flex;
justify-content: space-between;
align-items: center;
margin: 10px 0;
}
.next p,
.next h3 {
margin: 3px 0;
}
.forecstD {
margin: 20px 50px;
color: aliceblue;
}
.weekF {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
.cast-header {
color: aliceblue;
background: rgba(254, 189, 132, 0.539);
width: max-content;
padding: 5px 15px;
border-radius: 20px;
font-size: 18px;
margin-bottom: 5px;
}
.divider1,
.divider2 {
background: rgba(254, 189, 132, 0.539);
height: 200px;
border-radius: 5px;
}
.divider2 {
height: 5px;
width: 30%;
margin: 0 auto;
}
.time,
.date {
color: rgb(254, 189, 132);
}
.desc {
color: rgb(196, 255, 77);
}
//src/App.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import './App.css';
const apikey = "feff206daa60b539abe8fae8f2ab7f29";
function App() {
const [city, setCity] = useState('');
const [weatherData, setWeatherData] = useState(null);
useEffect(() => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
const { latitude, longitude } = position.coords;
const url = `http://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${apikey}`;
fetchWeatherData(url);
});
}
}, []);
const fetchWeatherData = async (url) => {
try {
const response = await axios.get(url);
const data = response.data;
console.log(data);
weatherReport(data);
setWeatherData(data);
// Send data to backend for storage
saveWeatherData(data);
} catch (error) {
console.error('Error fetching weather data:', error);
}
};
const searchByCity = async () => {
try {
const urlsearch = `http://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apikey}`;
const response = await axios.get(urlsearch);
const data = response.data;
console.log(data);
weatherReport(data);
setWeatherData(data);
// Send data to backend for storage
saveWeatherData(data);
} catch (error) {
console.error('Error fetching weather data:', error);
}
setCity('');
};
const saveWeatherData = async (data) => {
try {
const response = await axios.post('http://localhost:5000/api/weather', {
city: data.name,
country: data.sys.country,
temperature: Math.floor(data.main.temp - 273),
description: data.weather[0].description,
icon: data.weather[0].icon,
});
console.log('Weather data saved to database:', response.data);
} catch (error) {
console.error('Error saving weather data to database:', error);
}
};
const weatherReport = async (data) => {
const urlcast = `http://api.openweathermap.org/data/2.5/forecast?q=${data.name}&appid=${apikey}`;
try {
const response = await axios.get(urlcast);
const forecast = response.data;
console.log(forecast.city);
hourForecast(forecast);
dayForecast(forecast);
console.log(data);
document.getElementById('city').innerText = data.name + ', ' + data.sys.country;
console.log(data.name, data.sys.country);
console.log(Math.floor(data.main.temp - 273));
document.getElementById('temperature').innerText = Math.floor(data.main.temp - 273) + ' °C';
document.getElementById('clouds').innerText = data.weather[0].description;
console.log(data.weather[0].description);
let icon1 = data.weather[0].icon;
let iconurl = "http://api.openweathermap.org/img/w/" + icon1 + ".png";
document.getElementById('img').src = iconurl;
} catch (error) {
console.error('Error fetching forecast data:', error);
}
};
const hourForecast = (forecast) => {
document.querySelector('.templist').innerHTML = '';
for (let i = 0; i < 5; i++) {
var date = new Date(forecast.list[i].dt * 1000);
console.log((date.toLocaleTimeString(undefined, 'Asia/Kolkata')).replace(':00', ''));
let hourR = document.createElement('div');
hourR.setAttribute('class', 'next');
let div = document.createElement('div');
let time = document.createElement('p');
time.setAttribute('class', 'time');
time.innerText = (date.toLocaleTimeString(undefined, 'Asia/Kolkata')).replace(':00', '');
let temp = document.createElement('p');
temp.innerText = Math.floor((forecast.list[i].main.temp_max - 273)) + ' °C' + ' / ' + Math.floor((forecast.list[i].main.temp_min - 273)) + ' °C';
div.appendChild(time);
div.appendChild(temp);
let desc = document.createElement('p');
desc.setAttribute('class', 'desc');
desc.innerText = forecast.list[i].weather[0].description;
hourR.appendChild(div);
hourR.appendChild(desc);
document.querySelector('.templist').appendChild(hourR);
}
};
const dayForecast = (forecast) => {
document.querySelector('.weekF').innerHTML = '';
for (let i = 8; i < forecast.list.length; i += 8) {
console.log(forecast.list[i]);
let div = document.createElement('div');
div.setAttribute('class', 'dayF');
let day = document.createElement('p');
day.setAttribute('class', 'date');
day.innerText = new Date(forecast.list[i].dt * 1000).toDateString(undefined, 'Asia/Kolkata');
div.appendChild(day);
let temp = document.createElement('p');
temp.innerText = Math.floor((forecast.list[i].main.temp_max - 273)) + ' °C' + ' / ' + Math.floor((forecast.list[i].main.temp_min - 273)) + ' °C';
div.appendChild(temp);
let description = document.createElement('p');
description.setAttribute('class', 'desc');
description.innerText = forecast.list[i].weather[0].description;
div.appendChild(description);
document.querySelector('.weekF').appendChild(div);
}
};
return (
<div>
<div className="header">
<h1>WEATHER APP</h1>
<div>
<input
type="text"
name=""
id="input"
placeholder="Enter city name"
value={city}
onChange={(e) => setCity(e.target.value)}
/>
<button id="search" onClick={searchByCity}>
Search
</button>
</div>
</div>
<main>
<div className="weather">
<h2 id="city">Delhi,IN</h2>
<div className="temp-box">
<img src="/weathericon.png" alt="" id="img" />
<p id="temperature">26 °C</p>
</div>
<span id="clouds">Broken Clouds</span>
</div>
<div className="divider1"></div>
<div className="forecstH">
<p className="cast-header">Upcoming forecast</p>
<div className="templist">
{/* Hourly forecast will be rendered here */}
</div>
</div>
</main>
<div className="forecstD">
<div className="divider2"></div>
<p className="cast-header"> Next 4 days forecast</p>
<div className="weekF">
{/* Daily forecast will be rendered here */}
</div>
</div>
</div>
);
}
export default App;
To start frontend code:
npm start
Output:
Contact Us