Learn how to build a weather application with Replit in simple steps. Create, code, and deploy your own weather app easily using this guide.

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
To build a weather application on Replit, you should create a simple Node.js + Express backend that fetches weather data from a public API (like OpenWeatherMap) and a lightweight frontend that displays it. Store your API key securely in Replit Secrets, use fetch to request data, and serve a static HTML file through Express. This approach works reliably on Replit, stays responsive, and avoids exposing sensitive keys to the browser.
npm install express node-fetch
You’ll use Express to serve both static files and a server endpoint to call the weather API. Replace everything inside index.js with the following code:
import express from "express"
import fetch from "node-fetch"
const app = express()
const PORT = process.env.PORT || 3000
const API_KEY = process.env.WEATHER_API_KEY // stored securely
// Serve static files (the frontend)
app.use(express.static("public"))
// API endpoint that fetches weather data
app.get("/weather", async (req, res) => {
const city = req.query.city
if (!city) {
return res.status(400).json({ error: "City required" })
}
try {
const url = `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&appid=${API_KEY}&units=metric`
const response = await fetch(url)
const data = await response.json()
if (data.cod !== 200) {
return res.status(data.cod).json({ error: data.message })
}
res.json({
city: data.name,
temperature: data.main.temp,
condition: data.weather[0].description
})
} catch (err) {
console.error(err)
res.status(500).json({ error: "Failed to fetch weather data" })
}
})
app.listen(PORT, () => {
console.log(`Weather app running on port ${PORT}`)
})
This is the part users will see. Paste this into public/index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Weather App</title>
</head>
<body>
<h2>Weather Application</h2>
<input id="cityInput" type="text" placeholder="Enter city name" />
<button id="getWeatherBtn">Get Weather</button>
<p id="result"></p>
<script src="script.js"></script>
</body>
</html>
Now, add simple browser-side JavaScript to handle the button click and display results:
// Selecting HTML elements
const button = document.getElementById("getWeatherBtn")
const input = document.getElementById("cityInput")
const result = document.getElementById("result")
button.addEventListener("click", async () => {
const city = input.value.trim()
if (!city) {
result.textContent = "Please enter a city name."
return
}
result.textContent = "Loading..."
try {
const response = await fetch(`/weather?city=${encodeURIComponent(city)}`)
const data = await response.json()
if (data.error) {
result.textContent = `Error: ${data.error}`
return
}
result.textContent = `${data.city}: ${data.temperature}°C, ${data.condition}`
} catch (err) {
result.textContent = "Unable to fetch weather data."
}
})
console.log() generously for debugging inside Replit’s built-in console.process.env.PORT — Replit expects that for the web preview to work.
This setup uses Replit in the way it’s intended — lightweight server with static frontend, secured secrets, and easy collaborative coding — perfectly suited for a small but real-world-style 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 API Proxy Example</title>
</head>
<body>
<h1>Weather App Server (Node + Express)</h1>
<script type="module">
import express from "express";
import fetch from "node-fetch";
const app = express();
const PORT = process.env.PORT || 3000;
const API_KEY = process.env.OPENWEATHER_API\_KEY;
// Cache structure to avoid hitting API rate limits in Replit
const cache = new Map();
app.get("/api/weather/:city", async (req, res) => {
const city = req.params.city.toLowerCase();
const now = Date.now();
const cached = cache.get(city);
if (cached && now - cached.timestamp < 1000 _ 60 _ 5) {
return res.json(cached.data);
}
try {
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric`;
const response = await fetch(url);
if (!response.ok) throw new Error("API request failed");
const data = await response.json();
const structured = {
location: data.name,
temperature: data.main.temp,
description: data.weather[0].description,
humidity: data.main.humidity,
wind: data.wind.speed,
updatedAt: new Date().toISOString()
};
cache.set(city, { data: structured, timestamp: now });
res.json(structured);
} catch (err) {
res.status(500).json({ error: "Unable to fetch weather data" });
}
});
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weather GeoLocation Proxy</title>
</head>
<body>
<h1>Weather + Geolocation Backend Example</h1>
<script type="module">
import express from "express";
import fetch from "node-fetch";
const app = express();
const PORT = process.env.PORT || 3000;
const WEATHER_KEY = process.env.OPENWEATHER_API\_KEY;
const GEO_KEY = process.env.OPENCAGE_API\_KEY;
// Endpoint: user gives coordinates, server returns weather info with resolved city name
app.get("/api/weather/coords", async (req, res) => {
const { lat, lon } = req.query;
if (!lat || !lon) return res.status(400).json({ error: "Missing lat/lon" });
try {
// Reverse geocode the city name
const geoUrl = `https://api.opencagedata.com/geocode/v1/json?q=${lat}+${lon}&key=${GEO_KEY}`;
const geoResponse = await fetch(geoUrl);
if (!geoResponse.ok) throw new Error("Reverse geocoding failed");
const geoData = await geoResponse.json();
const city = geoData.results?.[0]?.components?.city || "Unknown location";
// Fetch weather info
const wUrl = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${WEATHER_KEY}&units=metric`;
const wResponse = await fetch(wUrl);
if (!wResponse.ok) throw new Error("Weather API failed");
const weatherData = await wResponse.json();
res.json({
location: city,
coordinates: { lat, lon },
temp: weatherData.main.temp,
desc: weatherData.weather[0].description,
humidity: weatherData.main.humidity
});
} catch (err) {
res.status(500).json({ error: err.message || "Server error" });
}
});
app.listen(PORT, () => console.log(`Server ready on ${PORT}`));
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weather Alert Scheduler</title>
</head>
<body>
<h1>Scheduled Weather Data Fetcher (Node + Express + Cron)</h1>
<script type="module">
import express from "express";
import fetch from "node-fetch";
import cron from "node-cron";
const app = express();
const PORT = process.env.PORT || 3000;
const WEATHER_API_KEY = process.env.OPENWEATHER_API_KEY;
let latestData = {};
async function fetchWeather(city = "London") {
try {
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${WEATHER_API_KEY}&units=metric`;
const res = await fetch(url);
if (!res.ok) throw new Error("Weather API failed");
const data = await res.json();
latestData[city] = {
temp: data.main.temp,
desc: data.weather[0].description,
updatedAt: new Date().toISOString()
};
console.log(`Updated weather cache for ${city}`);
} catch (error) {
console.error(`Error fetching weather for ${city}:`, error.message);
}
}
cron.schedule("_/10 _ _ _ \*", () => {
console.log("Scheduled weather refresh...");
fetchWeather("London");
fetchWeather("New York");
});
app.get("/api/weather/latest/:city", (req, res) => {
const city = req.params.city;
if (!latestData[city]) return res.status(404).json({ error: "No cached data" });
res.json(latestData[city]);
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
fetchWeather("London");
fetchWeather("New York");
});
</script>
</body>
</html>

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Building a weather app on Replit works best when you split cleanly between backend (a small Node.js or Python server handling API keys and weather data) and frontend (a simple HTML/JS page showing the weather). Store your weather API key in Replit Secrets, not in your code. Use the Replit web server to serve both static files (like HTML, CSS, JS) and your backend API route. Keep Replit’s always-on limits and timeout behavior in mind: use requests efficiently and don’t rely on background loops or cron jobs. Think of Replit like a live coding space that’s great for lightweight apps — not a full production hosting environment, but strong for prototyping and moderately used projects.
Create a basic folder structure like this:
weather-app/
├── index.js // Your Node.js server (backend)
├── public/
│ ├── index.html // Frontend HTML
│ ├── script.js // Frontend JavaScript
│ └── style.css // Optional styling
├── .replit // Replit config file
Replit automatically runs index.js as your entry point for Node.js projects. The public directory will hold your client‑side files.
In index.js, you’ll use Express.js to create a simple web server and expose an endpoint that fetches data from a weather API (like OpenWeatherMap). Install dependencies first inside Replit’s shell:
npm install express node-fetch
Then write your Node server:
import express from "express";
import fetch from "node-fetch";
const app = express();
// Serve files from the "public" folder
app.use(express.static("public"));
// Weather API endpoint
app.get("/api/weather", async (req, res) => {
const city = req.query.city;
const apiKey = process.env.WEATHER_API_KEY; // Key stored in Secrets
if (!city) {
return res.status(400).json({ error: "City is required" });
}
try {
// Fetch from OpenWeatherMap
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`
);
const data = await response.json();
res.json(data);
} catch (error) {
res.status(500).json({ error: "Failed to fetch weather data" });
}
});
app.listen(3000, () => console.log("Server running on port 3000"));
Insert this exact code into your index.js file. It runs on port 3000 by default and serves both your static files and a backend API route /api/weather. Replit will show a web preview button — clicking it opens your app.
In Replit, never put your API keys in plaintext. Click the padlock icon labeled “Secrets” in the left sidebar, create a new secret with the key name WEATHER_API_KEY and paste your actual weather API key as the value. Replit injects it into process.env when your code runs.
Create a new file public/index.html with a minimal interface and a text input for the city:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Weather App</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>Weather App</h1>
<input id="cityInput" type="text" placeholder="Enter city name" />
<button id="getWeatherBtn">Get Weather</button>
<div id="result"></div>
<script src="script.js"></script>
</body>
</html>
Then create public/script.js and add:
document.getElementById("getWeatherBtn").addEventListener("click", async () => {
const city = document.getElementById("cityInput").value;
const resultDiv = document.getElementById("result");
resultDiv.innerHTML = "Loading...";
try {
const res = await fetch(`/api/weather?city=${city}`);
const data = await res.json();
if (data.error) {
resultDiv.innerHTML = `<p>Error: ${data.error}</p>`;
return;
}
resultDiv.innerHTML = `
<h2>${data.name}</h2>
<p>Temperature: ${data.main.temp} °C</p>
<p>Weather: ${data.weather[0].description}</p>
`;
} catch (err) {
resultDiv.innerHTML = "<p>Failed to fetch weather data.</p>";
}
});
This frontend calls your Node.js backend instead of directly calling the weather API, which keeps your API key hidden on the server side — a key principle on Replit (and in web dev generally).
Following these steps, you’ll have a fully working weather app in Replit that securely fetches real weather data, keeps your API key safe, runs both backend and frontend in one Repl, and behaves reliably within Replit’s environment.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.