Skip to content

sruthichandanak/WeatherDashboard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

7 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🌦️ Weather Dashboard

A simple Weather Dashboard web app where users search a city to get real‑time weather using the OpenWeatherMap API. Searches are saved to a MySQL database. Frontend is HTML/CSS/JS; backend is Node.js + Express.


✨ Features

  • Search weather by city name
  • Shows temperature, humidity, wind, conditions
  • Search history persisted in MySQL
  • Simple, responsive UI
  • Clean REST API endpoints

🧰 Tech Stack

Frontend: HTML, CSS, JavaScript
Backend: Node.js, Express.js
Database: MySQL
API: OpenWeatherMap


πŸ“ Project Structure

weather-dashboard/
│── backend/
β”‚   β”œβ”€β”€ server.js        # Express app (serves API)
β”‚   β”œβ”€β”€ db.js            # MySQL connection
β”‚   └── routes.js        # API routes
β”‚
│── frontend/
β”‚   β”œβ”€β”€ index.html       # UI
β”‚   β”œβ”€β”€ style.css        # Styles
β”‚   └── script.js        # Client logic (fetches API)
β”‚
│── database/
β”‚   └── schema.sql       # DB schema (table: searches)
β”‚
│── .env                 # Environment variables
│── package.json         # Node dependencies/scripts
│── README.md            # You are here

πŸš€ Quick Start

1) Clone and install

git clone https://github.com/yourusername/weather-dashboard.git
cd weather-dashboard/backend
npm install

2) Create .env in backend/

PORT=5000
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=yourpassword
DB_NAME=weather_dashboard
API_KEY=your_openweathermap_api_key

3) Create MySQL database

CREATE DATABASE IF NOT EXISTS weather_dashboard;
USE weather_dashboard;

CREATE TABLE IF NOT EXISTS searches (
  id INT AUTO_INCREMENT PRIMARY KEY,
  city VARCHAR(100) NOT NULL,
  searched_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

You can run the SQL inside database/schema.sql as well.

4) Start the API

node server.js
# or
npm run start

5) Open the frontend

  • Open frontend/index.html in your browser (or serve it with a simple HTTP server).
  • Ensure the frontend calls your backend, e.g. http://localhost:5000.

πŸ”Œ API Endpoints

Base URL (local): http://localhost:5000

GET /api/weather?city=<CITY>

Fetch live weather for a city (proxies OpenWeatherMap and logs the search).
Response (example):

{
  "city": "Hyderabad",
  "temp": 30.5,
  "humidity": 62,
  "wind": 3.6,
  "condition": "Clouds",
  "icon": "04d",
  "timestamp": "2025-09-01T13:45:00Z"
}

GET /api/history

Returns recent searches from MySQL.
Response (example):

[
  {"id": 1, "city": "Hyderabad", "searched_at": "2025-09-01T13:40:10Z"},
  {"id": 2, "city": "Chennai",   "searched_at": "2025-09-01T13:42:33Z"}
]

πŸ—οΈ Environment Variables (backend/.env)

  • PORT – Express server port (default 5000)
  • DB_HOST, DB_USER, DB_PASSWORD, DB_NAME – MySQL connection
  • API_KEY – OpenWeatherMap API key

Sign up at OpenWeatherMap to get your API key.


πŸ§ͺ NPM Scripts (suggested)

Add these to backend/package.json:

{
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js",
    "lint": "eslint ."
  }
}

🧱 Minimal Backend Snippets

db.js

const mysql = require('mysql2/promise');

const pool = mysql.createPool({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  waitForConnections: true,
  connectionLimit: 10
});

module.exports = pool;

routes.js

const express = require('express');
const fetch = require('node-fetch');
const pool = require('./db');
const router = express.Router();

router.get('/weather', async (req, res) => {
  const city = (req.query.city || '').trim();
  if (!city) return res.status(400).json({ error: 'City is required' });

  try {
    const url = \`https://api.openweathermap.org/data/2.5/weather?q=\${encodeURIComponent(city)}&appid=\${process.env.API_KEY}&units=metric\`;
    const r = await fetch(url);
    if (!r.ok) return res.status(r.status).json({ error: 'Weather API error' });
    const data = await r.json();

    const payload = {
      city: data.name,
      temp: data.main.temp,
      humidity: data.main.humidity,
      wind: data.wind.speed,
      condition: data.weather?.[0]?.main || '',
      icon: data.weather?.[0]?.icon || ''
    };

    // log search
    await pool.execute('INSERT INTO searches (city) VALUES (?)', [payload.city]);

    res.json(payload);
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: 'Server error' });
  }
});

router.get('/history', async (_req, res) => {
  try {
    const [rows] = await pool.execute('SELECT id, city, searched_at FROM searches ORDER BY searched_at DESC LIMIT 20');
    res.json(rows);
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: 'Server error' });
  }
});

module.exports = router;

server.js

require('dotenv').config();
const express = require('express');
const cors = require('cors');
const routes = require('./routes');

const app = express();
app.use(cors());
app.use(express.json());

app.get('/', (_req, res) => res.json({ status: 'ok' }));
app.use('/api', routes);

const port = process.env.PORT || 5000;
app.listen(port, () => console.log('Server listening on', port));

πŸ–₯️ Minimal Frontend Snippet (frontend/script.js)

const form = document.querySelector('#searchForm');
const input = document.querySelector('#city');
const result = document.querySelector('#result');
const historyList = document.querySelector('#history');

const API_BASE = 'http://localhost:5000'; // change if deployed

async function fetchWeather(city) {
  const res = await fetch(\`\${API_BASE}/api/weather?city=\${encodeURIComponent(city)}\`);
  if (!res.ok) throw new Error('Failed to fetch weather');
  return res.json();
}

async function fetchHistory() {
  const res = await fetch(\`\${API_BASE}/api/history\`);
  if (!res.ok) return [];
  return res.json();
}

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const city = input.value.trim();
  if (!city) return;
  result.textContent = 'Loading...';
  try {
    const data = await fetchWeather(city);
    result.innerHTML = \`
      <h3>\${data.city}</h3>
      <p>Temp: \${data.temp} Β°C</p>
      <p>Humidity: \${data.humidity}%</p>
      <p>Wind: \${data.wind} m/s</p>
      <p>Condition: \${data.condition}</p>
    \`;
    loadHistory();
  } catch (err) {
    result.textContent = err.message;
  }
});

async function loadHistory() {
  const items = await fetchHistory();
  historyList.innerHTML = items.map(i => \`<li>\${i.city} β€” \${new Date(i.searched_at).toLocaleString()}</li>\`).join('');
}

loadHistory();

πŸš€ Deployment (Optional)

  • Frontend (Vercel/Netlify): Deploy frontend/ and set API_BASE to your backend URL.
  • Backend (Render/Railway): Deploy backend/, add environment variables from .env.
  • Database (Railway/PlanetScale/Aiven): Create a MySQL instance and update DB vars.

πŸ› οΈ Troubleshooting

  • ECONNREFUSED / ER_ACCESS_DENIED_ERROR: Check MySQL host/user/password and that MySQL is running.
  • CORS errors: Ensure cors() is enabled and API_BASE matches your backend URL.
  • 401/404 from OpenWeather: Verify API_KEY and city spelling.
  • History not updating: Confirm INSERT INTO searches is executed and table exists.

πŸ“œ License

MIT

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors