# A Step-by-Step Guide to Building a Full-Stack TypeScript Weather App with Node.js, React, and Tailwind CSS

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.

## Prerequisites:

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

1. [Node.js](https://nodejs.org/en) and npm (latest LTS version recommended)
    
2. A code editor like [Visual Studio Code](https://code.visualstudio.com/)
    
3. A basic understanding of TypeScript, React, and Node.js
    

### Step 1: Set up the project structure

First, let's create a new folder for our project and navigate into it:

```bash
mkdir weather-app
cd weather-app            
```

Next, create a `client` folder for the React app and a `server` folder for the Node.js server:

```bash
mkdir client server
```

### Step 2: Set up the Node.js server

Navigate to the `server` folder and initialize a new Node.js project with TypeScript:

```bash
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:

```json
{
  "compilerOptions": {
    "esModuleInterop": true,
    ...
  }
}
```

Install Express, Axios, and their respective types:

```bash
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:

```javascript
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:

```json
"scripts": {
  "start": "ts-node index.ts"
}
```

Test the server by running `npm start`. You should see the message "Server is running on port 3001".

### Step 3: Set up the React app

Navigate to the `client` folder and create a new React app using the `create-react-app` template:

```bash
cd ../client
npx create-react-app . --template typescript
```

Install the necessary dependencies:

```bash
npm install axios react-query tailwindcss@latest postcss@latest autoprefixer@latest
```

Initialize Tailwind CSS:

```bash
npx tailwindcss init
```

Add the following to your `tailwind.config.js` file:

```javascript
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:

```javascript
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}
```

Import the Tailwind CSS styles in the `src/index.css` file:

```css
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
```

### Step 4: Fetch and display weather data

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:

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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!
