A Step-by-Step Guide to Building a Full-Stack TypeScript Weather App with Node.js, React, and Tailwind CSS
I'm a developer from Greece looking to help others achieve their goals and reach financial and geographical freedom
Search for a command to run...
I'm a developer from Greece looking to help others achieve their goals and reach financial and geographical freedom
No comments yet. Be the first to comment.
That's right. It's not clickbait. And I'm gonna tell you exactly what I did. Time for a little history lesson. I've been a developer in Greece for over a decade. Although I've been able to earn a comfortable living for local standards, I always felt ...

Make yourself irresistible to top, global clients

It's honestly fine. I have over 11 years of professional web development experience and a Computer Engineer degree and when I started a new position at a big company about 2 months ago, I sucked. It took me 2 weeks to build a single screen in their R...

Frontend, backend, dev ops or something more?

In this guide, I will walk you through building a full-stack TypeScript weather app using Node.js, React, and Tailwind CSS. This app will fetch weather data from a third-party API, display the current weather, and provide a forecast for the next five days.
Before we begin, make sure you have the following installed on your system:
Node.js and npm (latest LTS version recommended)
A code editor like Visual Studio Code
A basic understanding of TypeScript, React, and Node.js
First, let's create a new folder for our project and navigate into it:
mkdir weather-app
cd weather-app
Next, create a client folder for the React app and a server folder for the Node.js server:
mkdir client server
Navigate to the server folder and initialize a new Node.js project with TypeScript:
cd server
npm init -y
npm install typescript ts-node @types/node --save-dev
npx tsc --init
Edit the tsconfig.json file to enable the "esModuleInterop" option:
{
"compilerOptions": {
"esModuleInterop": true,
...
}
}
Install Express, Axios, and their respective types:
npm install express axios
npm install @types/express @types/axios --save-dev
Create a file named index.ts in the server folder and set up a basic Express server:
import express from 'express';
const app = express();
const PORT = process.env.PORT || 3001;
app.get('/', (req, res) => {
res.send('Hello from the weather app server!');
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Add a start script to the server/package.json file:
"scripts": {
"start": "ts-node index.ts"
}
Test the server by running npm start. You should see the message "Server is running on port 3001".
Navigate to the client folder and create a new React app using the create-react-app template:
cd ../client
npx create-react-app . --template typescript
Install the necessary dependencies:
npm install axios react-query tailwindcss@latest postcss@latest autoprefixer@latest
Initialize Tailwind CSS:
npx tailwindcss init
Add the following to your tailwind.config.js file:
module.exports = {
purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
darkMode: false,
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
Configure PostCSS by creating a postcss.config.js file in the client folder:
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Import the Tailwind CSS styles in the src/index.css file:
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
Now, let's fetch the weather data from a third-party API, such as OpenWeatherMap. Sign up for a free account and get your API key.
In the server/index.ts file, add an endpoint to fetch weather data using Axios:
import axios from 'axios';
// Replace YOUR_API_KEY with your actual API key from OpenWeatherMap
const API_KEY = 'YOUR_API_KEY';
app.get('/weather/:city', async (req, res) => {
const city = req.params.city;
try {
const response = await axios.get(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric`
);
res.json(response.data);
} catch (error) {
res.status(500).send('Error fetching weather data');
}
});
In the client/src folder, create a new file named useWeather.ts to fetch weather data using React Query:
import { useQuery } from 'react-query';
import axios from 'axios';
const fetchWeather = async (city: string) => {
const response = await axios.get(`/weather/${city}`);
return response.data;
};
export const useWeather = (city: string) => {
return useQuery(['weather', city], () => fetchWeather(city));
};
Create a new WeatherCard.tsx component to display the weather data:
import React from 'react';
interface WeatherCardProps {
weatherData: any;
}
const WeatherCard: React.FC<WeatherCardProps> = ({ weatherData }) => {
return (
<div className="bg-white p-6 rounded-md shadow-md">
<h2 className="text-xl font-bold">
{weatherData.name}, {weatherData.sys.country}
</h2>
<p className="text-gray-600">{weatherData.weather[0].description}</p>
<p className="text-4xl font-bold">
{Math.round(weatherData.main.temp)}°C
</p>
</div>
);
};
export default WeatherCard;
Now, update the src/App.tsx file to fetch and display the weather data using the useWeather hook and WeatherCard component:
import React, { useState } from 'react';
import './App.css';
import WeatherCard from './WeatherCard';
import { useWeather } from './useWeather';
function App() {
const [city, setCity] = useState('London');
const { data, isLoading, isError } = useWeather(city);
const handleSearch = () => {
// Add your search logic here
};
return (
<div className="App">
<header className="App-header">
<h1>Weather App</h1>
<input
type="text"
placeholder="Search by city"
value={city}
onChange={(e) => setCity(e.target.value)}
/>
<button onClick={handleSearch}>Search</button>
{isLoading && <p>Loading...</p>}
{isError && <p>Error fetching weather data</p>}
{data && <WeatherCard weatherData={data} />}
</header>
</div>
);
}
export default App;
You now have a basic full-stack TypeScript weather app using Node.js, React, and Tailwind CSS. You can expand this app by adding a five-day forecast, error handling, and user input validation.
What would you add or change? Sign off in the comments!